Went through todos

This commit is contained in:
Dennis Eichhorn 2024-05-02 22:54:37 +00:00
parent f324ba454b
commit f19d0ad0cc
15 changed files with 142 additions and 25 deletions

View File

@ -469,9 +469,9 @@
"type": "TINYINT(1)",
"null": false
},
"billing_type_transfer_sign": {
"billing_type_sign": {
"description": "1 = from->to direction, -1 = to->from direction = credit note",
"name": "billing_type_transfer_sign",
"name": "billing_type_sign",
"type": "TINYINT(1)",
"null": false
},
@ -804,6 +804,14 @@
"type": "VARCHAR(10)",
"null": false
},
"billing_bill_rep": {
"name": "billing_bill_rep",
"type": "INT",
"null": true,
"default": null,
"foreignTable": "sales_rep",
"foreignKey": "sales_rep_id"
},
"billing_bill_referral": {
"name": "billing_bill_referral",
"type": "INT",

View File

@ -70,6 +70,28 @@ use phpOMS\Views\View;
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
*
* @feature Automatically create recurring bills (invoices, delivery notes, etc.)
* if a customer wants to receive items automatically
* https://github.com/Karaka-Management/oms-Billing/issues/7
*
* @feature Define approval workflow
* https://github.com/Karaka-Management/oms-Billing/issues/15
*
* @feature Allow to define re/usable templates (e.g. recurring invoices)
* https://github.com/Karaka-Management/oms-Billing/issues/30
*
* @feature Allow to define re/usable texts
* One idea could be to define a text item which has multiple l11n elements that can
* be used for this (e.g. item_invoice_desc_1, ..., item_invoice_desc_n).
* https://github.com/Karaka-Management/oms-Billing/issues/29
*
* @feature Batch print/export invoices based on filter
* https://github.com/Karaka-Management/oms-Billing/issues/27
*
* @feature Add BillTypeCategory which can be selected by the person creating a bill.
* This is just for internal use (e.g. posting invoice to a different client account)
* https://github.com/Karaka-Management/oms-Billing/issues/64
*/
final class ApiBillController extends Controller
{
@ -194,6 +216,13 @@ final class ApiBillController extends Controller
*
* @api
*
* @todo The bill archive should store the customer/supplier localized version since this is the official document
* https://github.com/Karaka-Management/oms-Billing/issues/5
*
* @feature Validate the client VAT before creating a invoice? This would also require a
* last_checked field to avoid multiple checks per day.
* https://github.com/Karaka-Management/oms-Billing/issues/44
*
* @since 1.0.0
*/
public function apiBillFinalize(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void
@ -428,6 +457,10 @@ final class ApiBillController extends Controller
// Example: payment plan or discounted and none-discounted date
// https://github.com/Karaka-Management/oms-Billing/issues/53
// @todo Make tax calculation based on invoice address, shipping from and shipping address... VERY complicated.
// Reason for this is Dreiecksgeschaeft
// https://github.com/Karaka-Management/oms-Billing/issues/66
if ($account instanceof Client) {
$bill->client = $account;
$bill->accTaxCode = empty($temp = $bill->client->getAttribute('sales_tax_code')->value->id) ? null : $temp;
@ -435,6 +468,7 @@ final class ApiBillController extends Controller
$bill->accSection = empty($temp = $bill->client->getAttribute('section')->value->id) ? null : $temp;
$bill->accGroup = empty($temp = $bill->client->getAttribute('client_group')->value->id) ? null : $temp;
$bill->accType = empty($temp = $bill->client->getAttribute('client_type')->value->id) ? null : $temp;
$bill->rep = $request->getDataInt('rep') ?? $account->rep;
} else {
$bill->supplier = $account;
$bill->accTaxCode = empty($temp = $bill->supplier->getAttribute('purchase_tax_code')->value->id) ? null : $temp;
@ -643,6 +677,24 @@ final class ApiBillController extends Controller
*
* @return Bill
*
* @todo Use bill and shipping address instead of main address if available
* https://github.com/Karaka-Management/oms-Billing/issues/45
*
* @feature Support multiple due dates for bills (e.g. payment plan, cash back)
* in invoice printing, backend and payment terms.
* https://github.com/Karaka-Management/oms-Billing/issues/53
*
* @feature Define default stock/stockType per unit per bill type
* https://github.com/Karaka-Management/oms-Billing/issues/62
*
* @question Always create delivery note (at least internally)?
* The reason is this way it would be easier to track when stock got moved (SD is horribly complex)
* Alternatively, check if line item references delivery note
* https://github.com/Karaka-Management/oms-Billing/issues/63
*
* @feature Add custom tax id for bill to manually overwrite the client_sales_tax_code.
* https://github.com/Karaka-Management/oms-Billing/issues/65
*
* @since 1.0.0
*/
public function createBillFromRequest(RequestAbstract $request, ResponseAbstract $response, $data = null) : Bill
@ -782,6 +834,10 @@ final class ApiBillController extends Controller
*
* @api
*
* @bug Only allow to remove media from bill if it is NOT system generated
* (invoices should not be possible to get removed from a bill)
* https://github.com/Karaka-Management/oms-Billing/issues/48
*
* @since 1.0.0
*/
public function apiMediaRemoveFromBill(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void

View File

@ -43,6 +43,12 @@ use phpOMS\Stdlib\Base\FloatInt;
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
*
* @feature Import client prices from csv/excel sheet
* https://github.com/Karaka-Management/oms-ClientManagement/issues/17
*
* @feature Perform inflation increase on all client prices
* https://github.com/Karaka-Management/oms-ClientManagement/issues/18
*/
final class ApiPriceController extends Controller
{
@ -255,10 +261,16 @@ final class ApiPriceController extends Controller
$basePrice ??= new NullPrice();
// @todo implement prices which cannot be improved even if there are better prices available
// @todo Implement prices which cannot be improved even if there are better prices available
// (i.e. some customer groups may not get better prices, Dentagen Beispiel)
// alternatively set prices as 'improvable' => which whitelists a price as can be improved
// or 'always_improves' which always overwrites other prices
// https://github.com/Karaka-Management/oms-Billing/issues/71
// @todo Quantity discounts are currently always 100%.
// There should be a way to define:
// "buy 2, get an additional one for 50% less"? Note sure if that is really necessary but think about it.
// https://github.com/Karaka-Management/oms-Billing/issues/69
// Find best price
$bestPrice = $basePrice;

View File

@ -35,6 +35,9 @@ use phpOMS\System\SystemUtils;
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
*
* @feature Define approval workflow
* https://github.com/Karaka-Management/oms-Billing/issues/15
*/
final class ApiPurchaseController extends Controller
{
@ -236,6 +239,9 @@ final class ApiPurchaseController extends Controller
*
* @throws \Exception
*
* @todo OCR line parsing
* https://github.com/Karaka-Management/oms-Billing/issues/17
*
* @since 1.0.0
*/
public function apiInvoiceParse(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void

View File

@ -211,6 +211,25 @@ final class BackendController extends Controller
*
* @return RenderableInterface
*
* @todo Show invoice PDF in preview on change
* We don't want to create a preview every time but only, when preview tab is open and if a change was made
* https://github.com/Karaka-Management/oms-Billing/issues/20
*
* @todo Show bill relations (on tab which shows all related bills)
* https://github.com/Karaka-Management/oms-Billing/issues/37
*
* @todo Create send as email button inside the bill. this opens the send email
* view where the email is pre-written with the attached PDF
* https://github.com/Karaka-Management/oms-Billing/issues/36
*
* @security Check if user is allowed to see invoice
* This is mostly relevant for customers seeing their own invoices since for employees this is automatically handled
* Customers are not generally allowed to see invoices (maybe even employees are ano allowed to see all?)
* https://github.com/Karaka-Management/oms-Billing/issues/49
*
* @todo Add specification for delivery terms (e.g. name of the port etc.). By default same as delivery address city
* https://github.com/Karaka-Management/oms-Billing/issues/70
*
* @since 1.0.0
* @codeCoverageIgnore
*/

View File

@ -17,6 +17,7 @@ namespace Modules\Billing\Models;
use Modules\Admin\Models\Account;
use Modules\Admin\Models\NullAccount;
use Modules\ClientManagement\Models\Client;
use Modules\Sales\Models\SalesRep;
use Modules\SupplierManagement\Models\Supplier;
use phpOMS\Localization\ISO4217CharEnum;
use phpOMS\Localization\ISO639x1Enum;
@ -143,6 +144,8 @@ class Bill implements \JsonSerializable
public string $accountNumber = '';
public ?SalesRep $rep = null;
/**
* Receiver.
*
@ -244,6 +247,8 @@ class Bill implements \JsonSerializable
/**
* Person referring for this order.
*
* Usually the sales rep
*
* @var Account
* @since 1.0.0
*/

View File

@ -19,6 +19,7 @@ use Modules\Billing\Models\Attribute\BillAttributeMapper;
use Modules\ClientManagement\Models\ClientMapper;
use Modules\Editor\Models\EditorDocMapper;
use Modules\Media\Models\MediaMapper;
use Modules\Sales\Models\SalesRepMapper;
use Modules\SupplierManagement\Models\SupplierMapper;
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
@ -75,6 +76,7 @@ class BillMapper extends DataMapperFactory
'billing_bill_netdiscount' => ['name' => 'billing_bill_netdiscount', 'type' => 'Serializable', 'internal' => 'netDiscount'],
'billing_bill_taxp' => ['name' => 'billing_bill_taxp', 'type' => 'Serializable', 'internal' => 'taxP'],
'billing_bill_fiaccount' => ['name' => 'billing_bill_fiaccount', 'type' => 'string', 'internal' => 'fiAccount'],
'billing_bill_rep' => ['name' => 'billing_bill_rep', 'type' => 'int', 'internal' => 'rep'],
'billing_bill_currency' => ['name' => 'billing_bill_currency', 'type' => 'string', 'internal' => 'currency'],
'billing_bill_language' => ['name' => 'billing_bill_language', 'type' => 'string', 'internal' => 'language'],
'billing_bill_referral' => ['name' => 'billing_bill_referral', 'type' => 'int', 'internal' => 'referral'],
@ -142,6 +144,10 @@ class BillMapper extends DataMapperFactory
'mapper' => AccountMapper::class,
'external' => 'billing_bill_referral',
],
'rep' => [
'mapper' => SalesRepMapper::class,
'external' => 'billing_bill_rep',
],
];
/**

View File

@ -45,7 +45,7 @@ final class BillTypeMapper extends DataMapperFactory
'billing_type_default_template' => ['name' => 'billing_type_default_template', 'type' => 'int', 'internal' => 'defaultTemplate'],
'billing_type_transfer_stock' => ['name' => 'billing_type_transfer_stock', 'type' => 'bool', 'internal' => 'transferStock'],
'billing_type_accounting' => ['name' => 'billing_type_accounting', 'type' => 'bool', 'internal' => 'isAccounting'],
'billing_type_transfer_sign' => ['name' => 'billing_type_transfer_sign', 'type' => 'int', 'internal' => 'sign'],
'billing_type_sign' => ['name' => 'billing_type_sign', 'type' => 'int', 'internal' => 'sign'],
'billing_type_email' => ['name' => 'billing_type_email', 'type' => 'bool', 'internal' => 'email'],
'billing_type_is_template' => ['name' => 'billing_type_is_template', 'type' => 'bool', 'internal' => 'isTemplate'],
];

View File

@ -443,7 +443,10 @@ class InvoiceRecognition
foreach ($lines as $row => $line) {
if (\preg_match($match, $line, $found) === 1) {
if ($row < $bestPos) {
// @todo don't many invoices have the due date at the bottom? bestPos doesn't make sense?!
// @todo When parsing invoices the best due date match is ranked by position.
// This makes little sense as due dates are defined sometimes at the top
// and sometimes at the bottom of an invoice
// https://github.com/Karaka-Management/oms-Billing/issues/54
$bestPos = $row;
$bestMatch = $found['bill_due'];
}

View File

@ -334,7 +334,7 @@ final class PurchaseBillMapper extends BillMapper
public static function getSupplierNetSales(int $supplier, \DateTime $start, \DateTime $end) : FloatInt
{
$sql = <<<SQL
SELECT SUM(billing_bill_netsales * billing_type_transfer_sign) as net_sales
SELECT SUM(billing_bill_netsales * billing_type_sign) as net_sales
FROM billing_bill
LEFT JOIN billing_type
ON billing_bill_type = billing_type_id
@ -379,7 +379,7 @@ final class PurchaseBillMapper extends BillMapper
public static function getSLVHistoric(int $supplier) : FloatInt
{
$sql = <<<SQL
SELECT SUM(billing_bill_netsales * billing_type_transfer_sign) as net_sales
SELECT SUM(billing_bill_netsales * billing_type_sign) as net_sales
FROM billing_bill
LEFT JOIN billing_type
ON billing_bill_type = billing_type_id
@ -400,8 +400,8 @@ final class PurchaseBillMapper extends BillMapper
{
$sql = <<<SQL
SELECT
SUM(billing_bill_netsales * billing_type_transfer_sign * -1) as net_sales,
SUM(billing_bill_netcosts * billing_type_transfer_sign * -1) as net_costs,
SUM(billing_bill_netsales * billing_type_sign * -1) as net_sales,
SUM(billing_bill_netcosts * billing_type_sign * -1) as net_costs,
YEAR(billing_bill_performance_date) as year,
MONTH(billing_bill_performance_date) as month
FROM billing_bill
@ -435,7 +435,7 @@ final class PurchaseBillMapper extends BillMapper
$sql = <<<SQL
SELECT
itemmgmt_attr_value_l11n_title as title,
SUM(billing_bill_element_total_netlistprice * billing_type_transfer_sign * -1) as net_sales
SUM(billing_bill_element_total_netlistprice * billing_type_sign * -1) as net_sales
FROM billing_bill
LEFT JOIN billing_type
ON billing_bill_type = billing_type_id
@ -478,7 +478,7 @@ final class PurchaseBillMapper extends BillMapper
->with('bill')
->with('bill/type')
->where('bill/supplier', $supplier)
->where('bill/type/transferStock', true)
->where('bill/type/isAccounting', true)
->executeGetArray();
}
}

View File

@ -257,7 +257,7 @@ final class SalesBillMapper extends BillMapper
{
$query = new Builder(self::$db);
$query->selectAs(ClientMapper::TABLE . '.clientmgmt_client_id', 'client')
->selectAs('SUM(' . BillElementMapper::TABLE . '.billing_bill_element_total_netsalesprice * billing_type_transfer_sign)', 'net_sales')
->selectAs('SUM(' . BillElementMapper::TABLE . '.billing_bill_element_total_netsalesprice * billing_type_sign)', 'net_sales')
->from(ClientMapper::TABLE)
->leftJoin(self::TABLE)
->on(ClientMapper::TABLE . '.clientmgmt_client_id', '=', self::TABLE . '.billing_bill_client')
@ -347,7 +347,7 @@ final class SalesBillMapper extends BillMapper
->with('bill')
->with('bill/type')
->where('bill/client', $client)
->where('bill/type/transferStock', true)
->where('bill/type/isAccounting', true)
->executeGetArray();
}
@ -360,7 +360,7 @@ final class SalesBillMapper extends BillMapper
$sql = <<<SQL
SELECT
billing_bill_billCountry as country,
SUM(billing_bill_element_total_netlistprice * billing_type_transfer_sign) as net_sales
SUM(billing_bill_element_total_netlistprice * billing_type_sign) as net_sales
FROM billing_bill
LEFT JOIN billing_type
ON billing_bill_type = billing_type_id
@ -409,8 +409,8 @@ final class SalesBillMapper extends BillMapper
$sql = <<<SQL
SELECT
billing_bill_element_item,
SUM(billing_bill_element_total_netsalesprice * billing_type_transfer_sign) as net_sales,
SUM(billing_bill_element_total_netpurchaseprice * billing_type_transfer_sign) as net_costs,
SUM(billing_bill_element_total_netsalesprice * billing_type_sign) as net_sales,
SUM(billing_bill_element_total_netpurchaseprice * billing_type_sign) as net_costs,
YEAR(billing_bill_performance_date) as year,
MONTH(billing_bill_performance_date) as month
FROM billing_bill_element
@ -484,8 +484,8 @@ final class SalesBillMapper extends BillMapper
{
$sql = <<<SQL
SELECT
SUM(billing_bill_netsales * billing_type_transfer_sign) as net_sales,
SUM(billing_bill_netcosts * billing_type_transfer_sign) as net_costs,
SUM(billing_bill_netsales * billing_type_sign) as net_sales,
SUM(billing_bill_netcosts * billing_type_sign) as net_costs,
YEAR(billing_bill_performance_date) as year,
MONTH(billing_bill_performance_date) as month
FROM billing_bill
@ -541,7 +541,7 @@ final class SalesBillMapper extends BillMapper
$sql = <<<SQL
SELECT
itemmgmt_attr_value_l11n_title as title,
SUM(billing_bill_element_total_netlistprice * billing_type_transfer_sign) as net_sales
SUM(billing_bill_element_total_netlistprice * billing_type_sign) as net_sales
FROM billing_bill
LEFT JOIN billing_type
ON billing_bill_type = billing_type_id
@ -632,7 +632,7 @@ final class SalesBillMapper extends BillMapper
public static function getClientNetSales(int $client, \DateTime $start, \DateTime $end) : FloatInt
{
$sql = <<<SQL
SELECT SUM(billing_bill_netsales * billing_type_transfer_sign) as net_sales
SELECT SUM(billing_bill_netsales * billing_type_sign) as net_sales
FROM billing_bill
LEFT JOIN billing_type
ON billing_bill_type = billing_type_id
@ -655,7 +655,7 @@ final class SalesBillMapper extends BillMapper
public static function getCLVHistoric(int $client) : FloatInt
{
$sql = <<<SQL
SELECT SUM(billing_bill_netsales * billing_type_transfer_sign) as net_sales
SELECT SUM(billing_bill_netsales * billing_type_sign) as net_sales
FROM billing_bill
LEFT JOIN billing_type
ON billing_bill_type = billing_type_id

View File

@ -21,4 +21,5 @@ return ['Navigation' => [
'Upload' => 'Hochladen',
'PaymentTerms' => 'Zahlungsbedingungen',
'ShippingTerms' => 'Lieferbedingungen',
'TaxCombinations' => 'Tax Combinations',
]];

View File

@ -21,4 +21,5 @@ return ['Navigation' => [
'Upload' => 'Upload',
'PaymentTerms' => 'Payment Terms',
'ShippingTerms' => 'Shipping Terms',
'TaxCombinations' => 'Tax Combinations',
]];

View File

@ -410,7 +410,7 @@ echo $this->data['nav']->render(); ?>
<div>
<div class="col-xs-12 col-sm-3 box">
<select id="iBillPreviewType" name="bill_preview_type"
data-action='[{"listener": "change", "action": [{"key": 1, "type": "dom.reload", "src": "iPreviewBill"}]}]'>
data-action='[{"listener": "change", "action": [{"key": 1, "type": "redirect", "uri": "{%}", "src": "iPreviewBill"}]}]'>
<?php foreach ($billTypes as $type) : ?>
<option value="<?= $type->id; ?>"<?= $type->id === $bill->type->id ? ' selected' : ''; ?>><?= $this->printHtml($type->getL11n()); ?>
<?php endforeach; ?>
@ -579,7 +579,7 @@ echo $this->data['nav']->render(); ?>
<?php endif; ?>
<td><a href="<?= $url; ?>"><?= $audit->trigger; ?></a>
<td><a class="content"
href="<?= UriFactory::build('{/base}/admin/account/settings?id=' . $audit->createdBy->id); ?>"><?= $this->printHtml(
href="<?= UriFactory::build('{/base}/admin/account/view?id=' . $audit->createdBy->id); ?>"><?= $this->printHtml(
$this->renderUserName('%3$s %2$s %1$s', [$audit->createdBy->name1, $audit->createdBy->name2, $audit->createdBy->name3, $audit->createdBy->login])
); ?></a>
<td><a href="<?= $url; ?>"><?= $audit->createdAt->format('Y-m-d H:i'); ?></a>

View File

@ -409,7 +409,7 @@ echo $this->data['nav']->render(); ?>
<div>
<div class="col-xs-12 col-sm-3 box">
<select id="iBillPreviewType" name="bill_preview_type"
data-action='[{"listener": "change", "action": [{"key": 1, "type": "dom.reload", "src": "iPreviewBill"}]}]'>
data-action='[{"listener": "change", "action": [{"key": 1, "type": "redirect", "uri": "{%}", "src": "iPreviewBill"}]}]'>
<?php foreach ($billTypes as $type) : ?>
<option value="<?= $type->id; ?>"<?= $type->id === $bill->type->id ? ' selected' : ''; ?>><?= $this->printHtml($type->getL11n()); ?>
<?php endforeach; ?>
@ -597,7 +597,7 @@ echo $this->data['nav']->render(); ?>
<?php endif; ?>
<td><a href="<?= $url; ?>"><?= $audit->trigger; ?></a>
<td><a class="content"
href="<?= UriFactory::build('{/base}/admin/account/settings?id=' . $audit->createdBy->id); ?>"><?= $this->printHtml(
href="<?= UriFactory::build('{/base}/admin/account/view?id=' . $audit->createdBy->id); ?>"><?= $this->printHtml(
$this->renderUserName('%3$s %2$s %1$s', [$audit->createdBy->name1, $audit->createdBy->name2, $audit->createdBy->name3, $audit->createdBy->login])
); ?></a>
<td><a href="<?= $url; ?>"><?= $audit->createdAt->format('Y-m-d H:i'); ?></a>