diff --git a/Admin/Install/Media/bill.pdf.php b/Admin/Install/Media/bill.pdf.php index b9ca0b3..b5ead04 100755 --- a/Admin/Install/Media/bill.pdf.php +++ b/Admin/Install/Media/bill.pdf.php @@ -73,8 +73,6 @@ $topPos = $pdf->getY(); // Set up default bill template $billTypeName = \strtoupper($bill->type->getL11n()); -// @todo depending on amount of lines, there is a solution (html, or use backtracking of tcpdf) - // Address $pdf->setY(50); $pdf->setFont('helvetica', '', 10); @@ -170,7 +168,7 @@ $header = [ $lang[$pdf->language]['Total'], ]; -$lines = $bill->getElements(); +$lines = $bill->elements; // Header $headerCount = \count($header); @@ -184,8 +182,8 @@ $first = true; // Data $fill = false; foreach($lines as $line) { - // @todo add support for empty lines (row = line) - if (/*$row === null || */$first || $pdf->getY() > $pdf->getPageHeight() - 40) { + // @todo depending on amount of lines, there is a solution (html, or use backtracking of tcpdf) + if ($first || $pdf->getY() > $pdf->getPageHeight() - 40) { $pdf->setFillColor(255, 162, 7); $pdf->setTextColor(255); $pdf->setDrawColor(255, 162, 7); @@ -209,24 +207,36 @@ foreach($lines as $line) { $first = false; } + // Discounts are shown below the original price -> additional line + // We don't want discount columns because that hints at customers they might be able to get discounts. + $lines = 1 + + ((int) ($line->discountQ->value > 0)) + + ((int) ($line->singleDiscountP->value > 0)) + + ((int) ($line->singleDiscountR->value > 0)); + $tempY = $pdf->getY(); - $pdf->writeHTMLCell($w[0], 10, null, null, $line->itemNumber . ' ' . $line->itemName, 0, 2, $fill); + //$pdf->writeHTMLCell($w[0], 10, null, null, $line->itemNumber . ' ' . $line->itemName, 0, 2, $fill); + $pdf->MultiCell($w[0], 10 * $lines, \trim($line->itemNumber . ' ' . $line->itemName), 0, 'L', $fill, 2, null, null, true, 0, true, true, 0, 'M', false); $height = $pdf->getY() - $tempY; - $singleSalesPriceNet = Money::fromFloatInt($line->singleSalesPriceNet); - $totalSalesPriceNet = Money::fromFloatInt($line->totalSalesPriceNet); + $singleListPriceNet = Money::fromFloatInt($line->singleListPriceNet); + $totalSalesPriceNet = Money::fromFloatInt($line->totalSalesPriceNet); - $pdf->MultiCell($w[1], $height, (string) $line->getQuantity(), 0, 'L', $fill, 0, 15 + $w[0], $tempY, true, 0, false, true, 0, 'M', true); - $pdf->MultiCell($w[2], $height, $singleSalesPriceNet->getCurrency(2, symbol: ''), 0, 'L', $fill, 0, 15 + $w[0] + $w[1], $tempY, true, 0, false, true, 0, 'M', true); - $pdf->MultiCell($w[3], $height, $totalSalesPriceNet->getCurrency(2, symbol: ''), 0, 'L', $fill, 1, 15 + $w[0] + $w[1] + $w[2], $tempY, true, 0, false, true, 0, 'M', true); + if ($line->quantity->value === 0) { + $pdf->MultiCell($w[1] + $w[2] + $w[3], $height, '', 0, 'L', $fill, 0, 15 + $w[0], $tempY, true, 0, false, true, 0, 'M', true); + } else { + $pdf->MultiCell($w[1], $height, (string) $line->quantity->getAmount($line->container->quantityDecimals), 0, 'L', $fill, 0, 15 + $w[0], $tempY, true, 0, false, true, 0, 'M', true); + $pdf->MultiCell($w[2], $height, $singleListPriceNet->getCurrency(2, symbol: ''), 0, 'L', $fill, 0, 15 + $w[0] + $w[1], $tempY, true, 0, false, true, 0, 'M', true); + $pdf->MultiCell($w[3], $height, $totalSalesPriceNet->getCurrency(2, symbol: ''), 0, 'L', $fill, 1, 15 + $w[0] + $w[1] + $w[2], $tempY, true, 0, false, true, 0, 'M', true); + } $fill = !$fill; // get taxes - if (!isset($taxes[$line->taxR->getInt() / 100])) { - $taxes[$line->taxR->getInt() / 100] = $line->taxP; + if (!isset($taxes[$line->taxR->value / 10000])) { + $taxes[$line->taxR->value / 10000] = $line->taxP; } else { - $taxes[$line->taxR->getInt() / 100]->add($line->taxP); + $taxes[$line->taxR->value / 10000]->add($line->taxP); } } diff --git a/Admin/Install/Messages.php b/Admin/Install/Messages.php index ddf2e5c..36ed926 100755 --- a/Admin/Install/Messages.php +++ b/Admin/Install/Messages.php @@ -18,7 +18,6 @@ use Modules\Billing\Models\SettingsEnum; use phpOMS\Application\ApplicationAbstract; use phpOMS\Message\Http\HttpRequest; use phpOMS\Message\Http\HttpResponse; -use phpOMS\Uri\HttpUri; /** * Media class. @@ -57,7 +56,7 @@ class Messages ]; $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('settings', \json_encode($settings)); diff --git a/Admin/Install/db.json b/Admin/Install/db.json index 4b6a523..f47abf1 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -428,6 +428,12 @@ "type": "TINYINT(1)", "null": false }, + "billing_type_accounting": { + "description": "Is this bill relevant for accounting", + "name": "billing_type_accounting", + "type": "TINYINT(1)", + "null": false + }, "billing_type_transfer_sign": { "description": "1 = from->to direction, -1 = to->from direction = credit note", "name": "billing_type_transfer_sign", @@ -553,17 +559,25 @@ }, "billing_bill_template": { "name": "billing_bill_template", - "type": "INT", - "null": true, - "default": null, - "foreignTable": "media", - "foreignKey": "media_id" + "type": "TINYINT(1)", + "null": false + }, + "billing_bill_archived": { + "name": "billing_bill_archived", + "type": "TINYINT(1)", + "null": false }, "billing_bill_account_no": { "name": "billing_bill_account_no", "type": "VARCHAR(50)", "null": false }, + "billing_bill_tax_type": { + "name": "billing_bill_tax_type", + "type": "INT", + "null": true, + "default": null + }, "billing_bill_supplier": { "name": "billing_bill_supplier", "type": "INT", @@ -736,6 +750,11 @@ "type": "BIGINT", "null": false }, + "billing_bill_taxp": { + "name": "billing_bill_taxp", + "type": "BIGINT", + "null": false + }, "billing_bill_currency": { "name": "billing_bill_currency", "type": "VARCHAR(3)", @@ -749,6 +768,11 @@ "foreignTable": "language", "foreignKey": "language_639_1" }, + "billing_bill_fiaccount": { + "name": "billing_bill_fiaccount", + "type": "VARCHAR(10)", + "null": false + }, "billing_bill_referral": { "name": "billing_bill_referral", "type": "INT", @@ -768,7 +792,36 @@ "type": "INT", "null": false }, + "billing_bill_accsegment": { + "description": "attribute values", + "name": "billing_bill_accsegment", + "type": "INT", + "null": true, + "default": null + }, + "billing_bill_accsection": { + "description": "attribute values", + "name": "billing_bill_accsection", + "type": "INT", + "null": true, + "default": null + }, + "billing_bill_accgroup": { + "description": "attribute values", + "name": "billing_bill_accgroup", + "type": "INT", + "null": true, + "default": null + }, + "billing_bill_acctype": { + "description": "attribute values", + "name": "billing_bill_acctype", + "type": "INT", + "null": true, + "default": null + }, "billing_bill_payment": { + "description": "should this handle the dues?", "name": "billing_bill_payment", "type": "INT", "null": false @@ -906,6 +959,14 @@ "foreignTable": "itemmgmt_item", "foreignKey": "itemmgmt_item_id" }, + "billing_bill_element_container": { + "name": "billing_bill_element_container", + "type": "INT", + "null": false, + "default": null, + "foreignTable": "itemmgmt_item_container", + "foreignKey": "itemmgmt_item_container_id" + }, "billing_bill_element_subscription": { "name": "billing_bill_element_subscription", "type": "INT", @@ -937,7 +998,7 @@ }, "billing_bill_element_quantity": { "name": "billing_bill_element_quantity", - "type": "INT", + "type": "BIGINT", "null": false }, "billing_bill_element_single_netlistprice": { @@ -970,6 +1031,12 @@ "null": true, "default": null }, + "billing_bill_element_single_effectivenetsalesprice": { + "name": "billing_bill_element_single_effectivenetsalesprice", + "type": "BIGINT", + "null": true, + "default": null + }, "billing_bill_element_single_grosssalesprice": { "name": "billing_bill_element_single_grosssalesprice", "type": "BIGINT", @@ -1108,6 +1175,51 @@ "null": true, "default": null }, + "billing_bill_element_segment": { + "description": "attribute values", + "name": "billing_bill_element_segment", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "itemmgmt_attr_value", + "foreignKey": "itemmgmt_attr_value_id" + }, + "billing_bill_element_section": { + "description": "attribute values", + "name": "billing_bill_element_section", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "itemmgmt_attr_value", + "foreignKey": "itemmgmt_attr_value_id" + }, + "billing_bill_element_salesgroup": { + "description": "attribute values", + "name": "billing_bill_element_salesgroup", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "itemmgmt_attr_value", + "foreignKey": "itemmgmt_attr_value_id" + }, + "billing_bill_element_productgroup": { + "description": "attribute values", + "name": "billing_bill_element_productgroup", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "itemmgmt_attr_value", + "foreignKey": "itemmgmt_attr_value_id" + }, + "billing_bill_element_itemtype": { + "description": "attribute values", + "name": "billing_bill_element_itemtype", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "itemmgmt_attr_value", + "foreignKey": "itemmgmt_attr_value_id" + }, "billing_bill_element_bill": { "name": "billing_bill_element_bill", "type": "INT", @@ -1115,9 +1227,24 @@ "foreignTable": "billing_bill", "foreignKey": "billing_bill_id" }, + "billing_bill_element_fiaccount": { + "name": "billing_bill_element_fiaccount", + "type": "VARCHAR(10)", + "null": false + }, + "billing_bill_element_costcenter": { + "name": "billing_bill_element_costcenter", + "type": "VARCHAR(10)", + "null": false + }, + "billing_bill_element_costobject": { + "name": "billing_bill_element_costobject", + "type": "VARCHAR(10)", + "null": false + }, "billing_bill_element_promotion": { "name": "billing_bill_element_promotion", - "type": "INT", + "type": "VARCHAR(10)", "default": null, "null": true }, @@ -1165,6 +1292,11 @@ "type": "TINYINT(1)", "null": false }, + "billing_attr_type_internal": { + "name": "billing_attr_type_internal", + "type": "TINYINT(1)", + "null": false + }, "billing_attr_type_required": { "description": "Every item must have this attribute type if set to true.", "name": "billing_attr_type_required", diff --git a/Admin/Install/types.json b/Admin/Install/types.json index df385e9..0cf81ef 100755 --- a/Admin/Install/types.json +++ b/Admin/Install/types.json @@ -3,6 +3,8 @@ "name": "sales_offer", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 1, + "sign": 1, + "isAccounting": false, "transferStock": false, "isTemplate": false, "l11n": { @@ -14,6 +16,8 @@ "name": "sales_order_confirmation", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 1, + "sign": 1, + "isAccounting": false, "transferStock": false, "isTemplate": false, "l11n": { @@ -25,6 +29,8 @@ "name": "sales_delivery_note", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 1, + "sign": 1, + "isAccounting": false, "transferStock": true, "isTemplate": false, "l11n": { @@ -36,6 +42,8 @@ "name": "sales_invoice", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 1, + "sign": 1, + "isAccounting": true, "transferStock": false, "isTemplate": false, "l11n": { @@ -47,6 +55,8 @@ "name": "sales_proforma_invoice", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 1, + "sign": 1, + "isAccounting": false, "transferStock": false, "isTemplate": false, "l11n": { @@ -58,6 +68,8 @@ "name": "sales_credit_note", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 1, + "sign": -1, + "isAccounting": true, "transferStock": false, "isTemplate": false, "l11n": { @@ -69,6 +81,8 @@ "name": "sales_reverse_invoice", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 1, + "sign": -1, + "isAccounting": true, "transferStock": false, "isTemplate": false, "l11n": { @@ -80,6 +94,8 @@ "name": "purchase_offer", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 2, + "sign": -1, + "isAccounting": false, "transferStock": false, "isTemplate": false, "l11n": { @@ -87,10 +103,25 @@ "de": "Angebot" } }, + { + "name": "purchase_order", + "numberFormat": "{y}{type}-{m}{sequence}", + "transferType": 2, + "sign": -1, + "isAccounting": false, + "transferStock": false, + "isTemplate": false, + "l11n": { + "en": "Order", + "de": "Bestellung" + } + }, { "name": "purchase_order_confirmation", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 2, + "sign": -1, + "isAccounting": false, "transferStock": false, "isTemplate": false, "l11n": { @@ -102,6 +133,8 @@ "name": "purchase_delivery_note", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 2, + "sign": -1, + "isAccounting": false, "transferStock": true, "isTemplate": false, "l11n": { @@ -113,6 +146,8 @@ "name": "purchase_invoice", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 2, + "sign": -1, + "isAccounting": true, "transferStock": false, "isTemplate": false, "l11n": { @@ -124,6 +159,8 @@ "name": "purchase_proforma_invoice", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 2, + "sign": -1, + "isAccounting": false, "transferStock": false, "isTemplate": false, "l11n": { @@ -135,6 +172,8 @@ "name": "purchase_credit_note", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 2, + "sign": 1, + "isAccounting": true, "transferStock": false, "isTemplate": false, "l11n": { @@ -146,6 +185,8 @@ "name": "purchase_reverse_invoice", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 2, + "sign": 1, + "isAccounting": true, "transferStock": false, "isTemplate": false, "l11n": { @@ -157,6 +198,8 @@ "name": "stock_movement", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 4, + "sign": 1, + "isAccounting": false, "transferStock": false, "isTemplate": false, "l11n": { @@ -165,20 +208,37 @@ } }, { - "name": "stock_scrapping", + "name": "stock_decrease", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 4, - "transferStock": false, + "sign": -1, + "isAccounting": false, + "transferStock": true, "isTemplate": false, "l11n": { - "en": "Scrapping", - "de": "Verschrottung" + "en": "Stock Decrease", + "de": "Lagerausbuchung" } }, { - "name": "sales_subscritpion", + "name": "stock_increase", + "numberFormat": "{y}{type}-{m}{sequence}", + "transferType": 4, + "sign": 1, + "isAccounting": false, + "transferStock": true, + "isTemplate": false, + "l11n": { + "en": "Stock Increase", + "de": "Lagereinbuchung" + } + }, + { + "name": "sales_subscription", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 1, + "sign": 1, + "isAccounting": true, "transferStock": false, "isTemplate": false, "l11n": { @@ -187,36 +247,16 @@ } }, { - "name": "sales_template", - "numberFormat": "{y}{type}-{m}{sequence}", - "transferType": 1, - "transferStock": false, - "isTemplate": true, - "l11n": { - "en": "Template", - "de": "Vorlage" - } - }, - { - "name": "purchase_subscritpion", + "name": "purchase_subscription", "numberFormat": "{y}{type}-{m}{sequence}", "transferType": 2, + "sign": -1, + "isAccounting": true, "transferStock": false, "isTemplate": false, "l11n": { "en": "Subscription", "de": "Abonnement" } - }, - { - "name": "purchase_template", - "numberFormat": "{y}{type}-{m}{sequence}", - "transferType": 2, - "transferStock": false, - "isTemplate": true, - "l11n": { - "en": "Template", - "de": "Vorlage" - } } ] \ No newline at end of file diff --git a/Admin/Installer.php b/Admin/Installer.php index 5d2b2c3..a38d2c2 100755 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -24,7 +24,6 @@ use phpOMS\Message\Http\HttpRequest; use phpOMS\Message\Http\HttpResponse; use phpOMS\Module\InstallerAbstract; use phpOMS\Module\ModuleInfo; -use phpOMS\Uri\HttpUri; /** * Installer class. @@ -107,7 +106,7 @@ final class Installer extends InstallerAbstract } /** @var array $terms */ - $terms = \json_decode($fileContent, true); + $terms = \json_decode($fileContent, true); $paymentTypeArray = self::createPaymentTerms($app, $terms); /* Shipping terms */ @@ -117,7 +116,7 @@ final class Installer extends InstallerAbstract } /** @var array $terms */ - $terms = \json_decode($fileContent, true); + $terms = \json_decode($fileContent, true); $shippingTypeArray = self::createShippingTerms($app, $terms); } @@ -136,19 +135,21 @@ final class Installer extends InstallerAbstract /** @var array $billAttrType */ $billAttrType = []; - /** @var \Modules\Billing\Controller\ApiController $module */ - $module = $app->moduleManager->getModuleInstance('Billing'); + /** @var \Modules\Billing\Controller\ApiAttributeController $module */ + $module = $app->moduleManager->getModuleInstance('Billing', 'ApiAttribute'); /** @var array $attribute */ foreach ($attributes as $attribute) { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('name', $attribute['name'] ?? ''); $request->setData('title', \reset($attribute['l11n'])); $request->setData('language', \array_keys($attribute['l11n'])[0] ?? 'en'); $request->setData('is_required', $attribute['is_required'] ?? false); + $request->setData('repeatable', $attribute['repeatable'] ?? false); + $request->setData('internal', $attribute['internal'] ?? false); $request->setData('custom', $attribute['is_custom_allowed'] ?? false); $request->setData('validation_pattern', $attribute['validation_pattern'] ?? ''); $request->setData('datatype', (int) $attribute['value_type']); @@ -173,7 +174,7 @@ final class Installer extends InstallerAbstract } $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('title', $l11n); @@ -203,8 +204,8 @@ final class Installer extends InstallerAbstract /** @var array $billAttrValue */ $billAttrValue = []; - /** @var \Modules\Billing\Controller\ApiController $module */ - $module = $app->moduleManager->getModuleInstance('Billing'); + /** @var \Modules\Billing\Controller\ApiAttributeController $module */ + $module = $app->moduleManager->getModuleInstance('Billing', 'ApiAttribute'); foreach ($attributes as $attribute) { $billAttrValue[$attribute['name']] = []; @@ -212,7 +213,7 @@ final class Installer extends InstallerAbstract /** @var array $value */ foreach ($attribute['values'] as $value) { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('value', $value['value'] ?? ''); @@ -246,7 +247,7 @@ final class Installer extends InstallerAbstract } $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('title', $l11n); @@ -275,8 +276,8 @@ final class Installer extends InstallerAbstract { $result = []; - /** @var \Modules\Billing\Controller\ApiController $module */ - $module = $app->moduleManager->getModuleInstance('Billing'); + /** @var \Modules\Billing\Controller\ApiTaxController $module */ + $module = $app->moduleManager->getModuleInstance('Billing', 'ApiTax'); /** @var \Modules\Attribute\Models\AttributeType $itemAttributeSales */ $itemAttributeSales = ItemAttributeTypeMapper::get() @@ -303,7 +304,7 @@ final class Installer extends InstallerAbstract : $supplierAttributeSales->getDefaultByValue($tax['account_code']); $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('tax_type', $tax['type']); @@ -343,23 +344,25 @@ final class Installer extends InstallerAbstract { $billTypes = []; - /** @var \Modules\Billing\Controller\ApiController $module */ - $module = $app->moduleManager->getModuleInstance('Billing'); + /** @var \Modules\Billing\Controller\ApiBillTypeController $module */ + $module = $app->moduleManager->getModuleInstance('Billing', 'ApiBillType'); // @todo allow multiple alternative bill templates // @todo implement ordering of templates foreach ($types as $type) { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('name', $type['name'] ?? ''); $request->setData('title', \reset($type['l11n'])); $request->setData('language', \array_keys($type['l11n'])[0] ?? 'en'); $request->setData('number_format', $type['numberFormat'] ?? '{id}'); + $request->setData('sign', $type['sign'] ?? 1); $request->setData('transfer_stock', $type['transferStock'] ?? false); $request->setData('is_template', $type['isTemplate'] ?? false); + $request->setData('is_accounting', $type['isAccounting'] ?? false); $request->setData('transfer_type', $type['transferType'] ?? BillTransferType::SALES); $request->setData('template', $template); @@ -384,7 +387,7 @@ final class Installer extends InstallerAbstract } $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('title', $l11n); @@ -419,7 +422,7 @@ final class Installer extends InstallerAbstract /** @var array $type */ foreach ($types as $type) { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('name', $type['name'] ?? ''); @@ -444,7 +447,7 @@ final class Installer extends InstallerAbstract } $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('title', $l11n); @@ -479,7 +482,7 @@ final class Installer extends InstallerAbstract /** @var array $type */ foreach ($types as $type) { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('name', $type['name'] ?? ''); @@ -504,7 +507,7 @@ final class Installer extends InstallerAbstract } $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('title', $l11n); diff --git a/Admin/Routes/Cli.php b/Admin/Routes/Cli.php index 278c7cc..cc9476b 100755 --- a/Admin/Routes/Cli.php +++ b/Admin/Routes/Cli.php @@ -4,7 +4,7 @@ declare(strict_types=1); use phpOMS\Router\RouteVerb; return [ - '^/billing/bill/purchase/parse.*$' => [ + '^/billing/bill/purchase/parse(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\CliController:cliParseSupplierBill', 'verb' => RouteVerb::ANY, diff --git a/Admin/Routes/Web/Api.php b/Admin/Routes/Web/Api.php index ebe8ff4..2c168d3 100755 --- a/Admin/Routes/Web/Api.php +++ b/Admin/Routes/Web/Api.php @@ -18,7 +18,7 @@ use phpOMS\Account\PermissionType; use phpOMS\Router\RouteVerb; return [ - '^.*/bill/render\?.*$' => [ + '^.*/bill/render(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\ApiBillController:apiMediaRender', 'verb' => RouteVerb::GET, @@ -29,7 +29,7 @@ return [ ], ], ], - '^.*/bill/render/preview.*$' => [ + '^.*/bill/render/preview(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\ApiBillController:apiPreviewRender', 'verb' => RouteVerb::GET, @@ -40,7 +40,7 @@ return [ ], ], ], - '^.*/bill/price.*$' => [ + '^.*/bill/price(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\ApiPriceController:apiPriceCreate', 'verb' => RouteVerb::GET, diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index 48fcc96..98d7e08 100755 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -18,7 +18,7 @@ use phpOMS\Account\PermissionType; use phpOMS\Router\RouteVerb; return [ - '^.*/sales/bill/create.*$' => [ + '^.*/sales/bill/create(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingSalesInvoiceCreate', 'verb' => RouteVerb::GET, @@ -29,7 +29,7 @@ return [ ], ], ], - '^.*/sales/bill/list.*$' => [ + '^.*/sales/bill/list(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingSalesList', 'verb' => RouteVerb::GET, @@ -40,7 +40,7 @@ return [ ], ], ], - '^.*/sales/bill\?.*$' => [ + '^.*/sales/bill(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingSalesInvoice', 'verb' => RouteVerb::GET, @@ -52,7 +52,7 @@ return [ ], ], - '^.*/purchase/bill/create.*$' => [ + '^.*/purchase/bill/create(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingPurchaseInvoiceCreate', 'verb' => RouteVerb::GET, @@ -63,7 +63,7 @@ return [ ], ], ], - '^.*/purchase/bill/list.*$' => [ + '^.*/purchase/bill/list(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingPurchaseList', 'verb' => RouteVerb::GET, @@ -74,7 +74,7 @@ return [ ], ], ], - '^.*/purchase/bill\?.*$' => [ + '^.*/purchase/bill(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingPurchaseInvoice', 'verb' => RouteVerb::GET, @@ -85,7 +85,7 @@ return [ ], ], ], - '^.*/purchase/bill/upload\?.*$' => [ + '^.*/purchase/bill/upload(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingPurchaseInvoiceUpload', 'verb' => RouteVerb::GET, @@ -97,7 +97,7 @@ return [ ], ], - '^.*/warehouse/bill/create.*$' => [ + '^.*/warehouse/bill/create(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingStockInvoiceCreate', 'verb' => RouteVerb::GET, @@ -108,7 +108,7 @@ return [ ], ], ], - '^.*/warehouse/bill/list.*$' => [ + '^.*/warehouse/bill/list(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingStockList', 'verb' => RouteVerb::GET, @@ -119,7 +119,7 @@ return [ ], ], ], - '^.*/warehouse/bill\?.*$' => [ + '^.*/warehouse/bill(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingStockInvoice', 'verb' => RouteVerb::GET, @@ -131,7 +131,7 @@ return [ ], ], - '^.*/private/purchase/recognition/dashboard.*$' => [ + '^.*/private/purchase/recognition/dashboard(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewPrivatePurchaseBillDashboard', 'verb' => RouteVerb::GET, @@ -142,7 +142,7 @@ return [ ], ], ], - '^.*/private/purchase/recognition/upload.*$' => [ + '^.*/private/purchase/recognition/upload(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewPrivatePurchaseBillUpload', 'verb' => RouteVerb::GET, @@ -153,7 +153,7 @@ return [ ], ], ], - '^.*/private/purchase/recognition/bill.*$' => [ + '^.*/private/purchase/recognition/bill(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewPrivateBillingPurchaseInvoice', 'verb' => RouteVerb::GET, @@ -164,7 +164,7 @@ return [ ], ], ], - '^.*/purchase/recognition/dashboard.*$' => [ + '^.*/purchase/recognition/dashboard(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewPrivatePurchaseBillDashboard', 'verb' => RouteVerb::GET, @@ -175,7 +175,7 @@ return [ ], ], ], - '^.*/purchase/recognition/upload.*$' => [ + '^.*/purchase/recognition/upload(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewPrivatePurchaseBillUpload', 'verb' => RouteVerb::GET, @@ -186,7 +186,7 @@ return [ ], ], ], - '^.*/purchase/recognition/bill.*$' => [ + '^.*/purchase/recognition/bill(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewPrivateBillingPurchaseInvoice', 'verb' => RouteVerb::GET, @@ -198,7 +198,7 @@ return [ ], ], - '^.*/bill/payment/list.*$' => [ + '^.*/bill/payment/list(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewPaymentList', 'verb' => RouteVerb::GET, @@ -209,7 +209,7 @@ return [ ], ], ], - '^.*/bill/payment/view.*$' => [ + '^.*/bill/payment/view(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewPaymentView', 'verb' => RouteVerb::GET, @@ -220,7 +220,7 @@ return [ ], ], ], - '^.*/bill/shipping/list.*$' => [ + '^.*/bill/shipping/list(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewShippingList', 'verb' => RouteVerb::GET, @@ -231,7 +231,7 @@ return [ ], ], ], - '^.*/bill/shipping/view.*$' => [ + '^.*/bill/shipping/view(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewShippingView', 'verb' => RouteVerb::GET, diff --git a/Controller/ApiAttributeController.php b/Controller/ApiAttributeController.php index e493871..4ac3795 100755 --- a/Controller/ApiAttributeController.php +++ b/Controller/ApiAttributeController.php @@ -61,12 +61,15 @@ final class ApiAttributeController extends Controller return; } - $type = BillAttributeTypeMapper::get()->with('defaults')->where('id', (int) $request->getData('type'))->execute(); + $type = BillAttributeTypeMapper::get() + ->with('defaults') + ->where('id', (int) $request->getData('type')) + ->execute(); if (!$type->repeatable) { $attr = BillAttributeMapper::count() ->with('type') - ->where('type/id', (int) $request->getData('type')) + ->where('type/id', $type->id) ->where('ref', (int) $request->getData('ref')) ->execute(); @@ -164,13 +167,20 @@ final class ApiAttributeController extends Controller ->where('id', $request->getDataInt('type') ?? 0) ->execute(); + if ($type->isInternal) { + $response->header->status = RequestStatusCode::R_403; + $this->createInvalidCreateResponse($request, $response, $val); + + return; + } + $attrValue = $this->createAttributeValueFromRequest($request, $type); $this->createModel($request->header->account, $attrValue, BillAttributeValueMapper::class, 'attr_value', $request->getOrigin()); if ($attrValue->isDefault) { $this->createModelRelation( $request->header->account, - (int) $request->getData('type'), + $type->id, $attrValue->id, BillAttributeTypeMapper::class, 'defaults', '', $request->getOrigin() ); diff --git a/Controller/ApiBillController.php b/Controller/ApiBillController.php index dce666e..8df01ea 100755 --- a/Controller/ApiBillController.php +++ b/Controller/ApiBillController.php @@ -16,11 +16,15 @@ namespace Modules\Billing\Controller; use Modules\Admin\Models\NullAccount; use Modules\Admin\Models\SettingsEnum as AdminSettingsEnum; +use Modules\Attribute\Models\NullAttribute; +use Modules\Attribute\Models\NullAttributeType; +use Modules\Attribute\Models\NullAttributeValue; use Modules\Billing\Models\Bill; use Modules\Billing\Models\BillElement; use Modules\Billing\Models\BillElementMapper; use Modules\Billing\Models\BillMapper; use Modules\Billing\Models\BillStatus; +use Modules\Billing\Models\BillTransferType; use Modules\Billing\Models\BillTypeMapper; use Modules\Billing\Models\NullBill; use Modules\Billing\Models\NullBillElement; @@ -28,8 +32,10 @@ use Modules\Billing\Models\PermissionCategory; use Modules\Billing\Models\SettingsEnum; use Modules\ClientManagement\Models\Client; use Modules\ClientManagement\Models\ClientMapper; +use Modules\ItemManagement\Models\Attribute\ItemAttributeMapper; use Modules\ItemManagement\Models\Item; use Modules\ItemManagement\Models\ItemMapper; +use Modules\ItemManagement\Models\NullContainer; use Modules\Media\Models\CollectionMapper; use Modules\Media\Models\Media; use Modules\Media\Models\MediaMapper; @@ -46,12 +52,14 @@ use phpOMS\Autoloader; use phpOMS\DataStorage\Database\Query\ColumnName; use phpOMS\Localization\ISO4217CharEnum; use phpOMS\Localization\ISO639x1Enum; +use phpOMS\Message\Http\HttpRequest; use phpOMS\Message\Http\RequestStatusCode; use phpOMS\Message\Mail\Email; use phpOMS\Message\NotificationLevel; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Model\Message\FormValidation; +use phpOMS\Stdlib\Base\FloatInt; use phpOMS\System\MimeType; use phpOMS\Views\View; @@ -72,13 +80,17 @@ final class ApiBillController extends Controller * * @since 1.0.0 */ - public function __construct(ApplicationAbstract $app = null) + public function __construct(?ApplicationAbstract $app = null) { parent::__construct($app); if ($this->app->moduleManager->isActive('WarehouseManagement')) { $this->app->eventManager->importFromFile(__DIR__ . '/../../WarehouseManagement/Admin/Hooks/Manual.php'); } + + if ($this->app->moduleManager->isActive('Accounting')) { + $this->app->eventManager->importFromFile(__DIR__ . '/../../Accounting/Admin/Hooks/Manual.php'); + } } /** @@ -205,6 +217,13 @@ final class ApiBillController extends Controller /** * Create a base Bill object with default values * + * Client attributes required: + * 'segment', 'section', 'client_group', 'client_type', + * 'sales_tax_code' + * + * Supplier attributes required: + * 'purchase_tax_code' + * * @param Client|Supplier $account The client or supplier object for whom the bill is being created * @param RequestAbstract $request The request object that contains the header account * @@ -227,18 +246,24 @@ final class ApiBillController extends Controller $bill->billDate = new \DateTime('now'); // @todo Date of payment $bill->performanceDate = $request->getDataDateTime('performancedate') ?? new \DateTime('now'); // @todo Date of payment $bill->accountNumber = $account->number; - $bill->setStatus($request->getDataInt('status') ?? BillStatus::DRAFT); + $bill->status = BillStatus::tryFromValue($request->getDataInt('status')) ?? BillStatus::DRAFT; - $bill->shippingTerms = null; - $bill->shippingText = ''; + $bill->shippingTerms = null; + $bill->shippingText = ''; - $bill->paymentTerms = null; - $bill->paymentText = ''; + $bill->paymentTerms = null; + $bill->paymentText = ''; if ($account instanceof Client) { - $bill->client = $account; + $bill->client = $account; + $bill->accTaxCode = empty($temp = $bill->client->getAttribute('sales_tax_code')->value->id) ? null : $temp; + $bill->accSegment = empty($temp = $bill->client->getAttribute('segment')->value->id) ? null : $temp; + $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; } else { - $bill->supplier = $account; + $bill->supplier = $account; + $bill->accTaxCode = empty($temp = $bill->supplier->getAttribute('purchase_tax_code')->value->id) ? null : $temp; } // @todo use bill and shipping address instead of main address if available @@ -246,9 +271,9 @@ final class ApiBillController extends Controller $bill->billAddress = $request->getDataString('billaddress') ?? $account->mainAddress->address; $bill->billCity = $request->getDataString('billtocity') ?? $account->mainAddress->city; $bill->billZip = $request->getDataString('billtopostal') ?? $account->mainAddress->postal; - $bill->billCountry = $request->getDataString('billtocountry') ?? $account->mainAddress->getCountry(); + $bill->billCountry = $request->getDataString('billtocountry') ?? $account->mainAddress->country; - $bill->setCurrency(ISO4217CharEnum::_EUR); + $bill->currency = ISO4217CharEnum::_EUR; /** @var \Model\Setting $settings */ $settings = $this->app->appSettings->get(null, @@ -283,7 +308,7 @@ final class ApiBillController extends Controller if (!empty($accountBillLanguage) && \in_array($accountBillLanguage, $validLanguages)) { $billLanguage = $accountBillLanguage; } else { - $accountLanguages = ISO639x1Enum::languageFromCountry($account->mainAddress->getCountry()); + $accountLanguages = ISO639x1Enum::languageFromCountry($account->mainAddress->country); $accountLanguage = empty($accountLanguages) ? '' : $accountLanguages[0]; if (\in_array($accountLanguage, $validLanguages)) { @@ -312,7 +337,11 @@ final class ApiBillController extends Controller /** * Create a base BillElement object with default values * - * @param Client $client The client object for whom the bill is being created + * Item attributes required: + * 'segment', 'section', 'sales_group', 'product_group', 'product_type', + * 'sales_tax_code', 'purchase_tax_code', 'costcenter', 'costobject', + * 'default_purchase_container', 'default_sales_container', + * * @param Item $item The item object for which the bill element is being created * @param Bill $bill The bill object for which the bill element is being created * @param RequestAbstract $request The request object that contains the header account @@ -321,17 +350,94 @@ final class ApiBillController extends Controller * * @since 1.0.0 */ - public function createBaseBillElement(Client $client, Item $item, Bill $bill, RequestAbstract $request) : BillElement + public function createBaseBillElement(Item $item, Bill $bill, RequestAbstract $request) : BillElement { - $taxCode = $this->app->moduleManager->get('Billing', 'ApiTax') - ->getTaxCodeFromClientItem($client, $item, $request->header->l11n->country); + // Handle person tax code for finding tax combination below + $attr = new NullAttribute(); + $attrType = new NullAttributeType(); + $attrType->name = $bill->client !== null ? 'sales_tax_code' : 'purchase_tax_code'; + $attrValue = new NullAttributeValue($bill->accTaxCode ?? 0); + $attr->type = $attrType; + $attr->value = $attrValue; - return BillElement::fromItem( + $container = $request->hasData('container') ? new NullContainer($request->getDataInt('container')) : null; + $attr = new NullAttribute(); + + if ($bill->type->transferType === BillTransferType::PURCHASE) { + $bill->supplier->attributes[] = $attr; + + if ($container === null) { + $attr = $item->getAttribute('default_purchase_container'); + if ($attr->id === 0) { + /** @var \Modules\Attribute\Models\Attribute $attr */ + $attr = ItemAttributeMapper::get() + ->with('type') + ->with('value') + ->where('ref', $item->id) + ->where('type/name', 'default_purchase_container') + ->execute(); + } + } + } else { + $bill->client->attributes[] = $attr; + + if ($container === null) { + $attr = $item->getAttribute('default_sales_container'); + if ($attr->id === 0) { + /** @var \Modules\Attribute\Models\Attribute $attr */ + $attr = ItemAttributeMapper::get() + ->with('type') + ->with('value') + ->where('ref', $item->id) + ->where('type/name', 'default_sales_container') + ->execute(); + } + } + } + + $container = $container === null && $attr->id !== 0 + ? new NullContainer($attr->value->getValue()) + : $container; + + $taxCombination = $this->app->moduleManager->get('Billing', 'ApiTax') + ->getTaxForPerson($bill->client, $bill->supplier, $item, $request->header->l11n->country); + + $element = BillElement::fromItem( $item, - $taxCode, - $request->getDataInt('quantity') ?? 1, - $bill->id + $taxCombination, + FloatInt::toInt($request->getDataString('quantity') ?? 1), + $bill->id, + $container ); + + $element->itemSegment = empty($temp = $item->getAttribute('segment')->value->id) ? null : $temp; + $element->itemSection = empty($temp = $item->getAttribute('section')->value->id) ? null : $temp; + $element->itemSalesGroup = empty($temp = $item->getAttribute('sales_group')->value->id) ? null : $temp; + $element->itemProductGroup = empty($temp = $item->getAttribute('product_group')->value->id) ? null : $temp; + $element->itemType = empty($temp = $item->getAttribute('product_type')->value->id) ? null : $temp; + + $internalRequest = new HttpRequest($request->uri); + $internalRequest->header->account = $request->header->account; + + $price = $this->app->moduleManager->get('Billing', 'ApiPrice')->findBestPrice($internalRequest, $item, $bill->client, $bill->supplier); + + $element->singleListPriceNet->value = $price['bestPrice']->value === 0 + ? $item->salesPrice->value + : $price['bestPrice']->value; + + $element->singleSalesPriceNet->value = $price['bestActualPrice']->value === 0 + ? $item->salesPrice->value + : $price['bestActualPrice']->value; + + $element->singleDiscountP->value = ($price['discountAmount']->value ?? 0) / ($element->quantity->value - $price['bonus']->value); + $element->totalDiscountP = $price['discountAmount']; + + $element->singleDiscountR = $price['discountPercent']; + $element->discountQ = $price['bonus']; + + $element->recalculatePrices(); + + return $element; } /** @@ -354,7 +460,14 @@ final class ApiBillController extends Controller $account = ClientMapper::get() ->with('account') ->with('mainAddress') + ->with('attributes') + ->with('attributes/type') + ->with('attributes/value') ->where('id', (int) $request->getData('client')) + ->where('attributes/type/name', [ + 'segment', 'section', 'client_group', 'client_type', + 'sales_tax_code', + ], 'IN') ->execute(); } elseif (($request->getDataInt('supplier') ?? -1) === 0) { /** @var \Modules\SupplierManagement\Models\Supplier $account */ @@ -364,7 +477,13 @@ final class ApiBillController extends Controller $account = SupplierMapper::get() ->with('account') ->with('mainAddress') + ->with('attributes') + ->with('attributes/type') + ->with('attributes/value') ->where('id', (int) $request->getData('supplier')) + ->where('attributes/type/name', [ + 'purchase_tax_code', + ], 'IN') ->execute(); } @@ -673,9 +792,7 @@ final class ApiBillController extends Controller /** @var \Modules\Billing\Models\Bill $old */ $old = BillMapper::get() ->with('client') - ->with('client/attributes') - ->with('client/attributes/type') - ->with('client/attributes/value') + ->with('supplier') ->where('id', $request->getDataInt('bill') ?? 0) ->execute(); @@ -691,7 +808,7 @@ final class ApiBillController extends Controller $new = clone $old; $new->addElement($element); - $this->updateModel($request->header->account, $old, $new, BillMapper::class, 'bill_element', $request->getOrigin()); + $this->updateModel($request->header->account, $old, $new, BillMapper::class, 'bill', $request->getOrigin()); $this->createStandardCreateResponse($request, $response, $element); } @@ -709,6 +826,8 @@ final class ApiBillController extends Controller */ private function createBillElementFromRequest(RequestAbstract $request, ResponseAbstract $response, Bill $bill, $data = null) : BillElement { + // @todo handle text element + /** @var \Modules\ItemManagement\Models\Item $item */ $item = ItemMapper::get() ->with('attributes') @@ -717,6 +836,11 @@ final class ApiBillController extends Controller ->with('l11n') ->with('l11n/type') ->where('id', $request->getDataInt('item') ?? 0) + ->where('attributes/type/name', [ + 'segment', 'section', 'sales_group', 'product_group', 'product_type', + 'sales_tax_code', 'purchase_tax_code', 'costcenter', 'costobject', + 'default_purchase_container', 'default_sales_container', + ], 'IN') ->where('l11n/type/title', ['name1', 'name2', 'name3'], 'IN') ->where('l11n/language', $bill->language) ->execute(); @@ -725,16 +849,9 @@ final class ApiBillController extends Controller return new NullBillElement(); } - $element = $this->createBaseBillElement($bill->client, $item, $bill, $request); + $element = $this->createBaseBillElement($item, $bill, $request); $element->bill = new NullBill($bill->id); - // discounts - // @todo implement a addDiscount function - /* - if ($request->getData('discount_percentage') !== null) { - } - */ - return $element; } @@ -796,6 +913,7 @@ final class ApiBillController extends Controller /** @var \Modules\Billing\Models\Bill $bill */ $bill = BillMapper::get() ->with('elements') + ->with('elements/container') ->where('id', $request->getDataInt('bill') ?? 0) ->execute(); @@ -849,7 +967,7 @@ final class ApiBillController extends Controller ->where('id', [ (int) $settings[AdminSettingsEnum::DEFAULT_TEMPLATES]->content, (int) $settings[AdminSettingsEnum::DEFAULT_ASSETS]->content, - $templateId + $templateId, ], 'IN') ->execute(); @@ -940,12 +1058,23 @@ final class ApiBillController extends Controller /** @var \Modules\Billing\Models\Bill $bill */ $bill = BillMapper::get() ->with('elements') + ->with('elements/container') ->with('type') ->with('type/l11n') ->where('id', $request->getDataInt('bill') ?? 0) ->where('type/l11n/language', new ColumnName(BillMapper::getColumnByMember('language'))) ->execute(); + $this->app->eventManager->triggerSimilar('PRE:Module:' . self::NAME . '-bill-finalize', '', [ + $request->header->account, + null, $bill, + null, self::NAME . '-bill-finalize', + self::NAME, + (string) $bill->id, + null, + $request->getOrigin(), + ]); + // Handle PDF generation $templateId = $request->getDataInt('bill_template') ?? $bill->type->defaultTemplate?->id ?? 0; @@ -981,7 +1110,7 @@ final class ApiBillController extends Controller ->where('id', [ (int) $settings[AdminSettingsEnum::DEFAULT_TEMPLATES]->content, (int) $settings[AdminSettingsEnum::DEFAULT_ASSETS]->content, - $templateId + $templateId, ], 'IN') ->execute(); diff --git a/Controller/ApiBillTypeController.php b/Controller/ApiBillTypeController.php index 121c557..c7b53e9 100755 --- a/Controller/ApiBillTypeController.php +++ b/Controller/ApiBillTypeController.php @@ -75,11 +75,16 @@ final class ApiBillTypeController extends Controller private function createBillTypeFromRequest(RequestAbstract $request) : BillType { $billType = new BillType($request->getDataString('name') ?? ''); - $billType->setL11n($request->getDataString('title') ?? '', $request->getDataString('language') ?? ISO639x1Enum::_EN); + $billType->setL11n( + $request->getDataString('title') ?? '', + ISO639x1Enum::tryFromValue($request->getDataString('language')) ?? ISO639x1Enum::_EN + ); $billType->numberFormat = $request->getDataString('number_format') ?? '{id}'; + $billType->sign = $request->getDataInt('sign') ?? 1; $billType->transferStock = $request->getDataBool('transfer_stock') ?? false; $billType->isTemplate = $request->getDataBool('is_template') ?? false; - $billType->transferType = $request->getDataInt('transfer_type') ?? BillTransferType::SALES; + $billType->isAccounting = $request->getDataBool('is_accounting') ?? false; + $billType->transferType = BillTransferType::tryFromValue($request->getDataInt('transfer_type')) ?? BillTransferType::SALES; $billType->defaultTemplate = $request->hasData('template') ? new NullCollection((int) $request->getData('template')) : null; @@ -150,12 +155,10 @@ final class ApiBillTypeController extends Controller */ private function createBillTypeL11nFromRequest(RequestAbstract $request) : BaseStringL11n { - $billTypeL11n = new BaseStringL11n(); - $billTypeL11n->ref = $request->getDataInt('type') ?? 0; - $billTypeL11n->setLanguage( - $request->getDataString('language') ?? $request->header->l11n->language - ); - $billTypeL11n->content = $request->getDataString('title') ?? ''; + $billTypeL11n = new BaseStringL11n(); + $billTypeL11n->ref = $request->getDataInt('type') ?? 0; + $billTypeL11n->language = ISO639x1Enum::tryFromValue($request->getDataString('language')) ?? $request->header->l11n->language; + $billTypeL11n->content = $request->getDataString('title') ?? ''; return $billTypeL11n; } @@ -228,7 +231,8 @@ final class ApiBillTypeController extends Controller $new->numberFormat = $request->getDataString('number_format') ?? $new->numberFormat; $new->transferStock = $request->getDataBool('transfer_stock') ?? $new->transferStock; $new->isTemplate = $request->getDataBool('is_template') ?? $new->isTemplate; - $new->transferType = $request->getDataInt('transfer_type') ?? $new->transferType; + $new->isAccounting = $request->getDataBool('is_accounting') ?? $new->isAccounting; + $new->transferType = BillTransferType::tryFromValue($request->getDataInt('transfer_type')) ?? $new->transferType; $new->defaultTemplate = $request->hasData('template') ? new NullCollection((int) $request->getData('template')) : $new->defaultTemplate; @@ -346,11 +350,9 @@ final class ApiBillTypeController extends Controller */ public function updateBillTypeL11nFromRequest(RequestAbstract $request, BaseStringL11n $new) : BaseStringL11n { - $new->ref = $request->getDataInt('type') ?? $new->ref; - $new->setLanguage( - $request->getDataString('language') ?? $new->language - ); - $new->content = $request->getDataString('title') ?? $new->content; + $new->ref = $request->getDataInt('type') ?? $new->ref; + $new->language = ISO639x1Enum::tryFromValue($request->getDataString('language')) ?? $new->language; + $new->content = $request->getDataString('title') ?? $new->content; return $new; } diff --git a/Controller/ApiController.php b/Controller/ApiController.php index ad39f58..6a6ebe7 100755 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -36,347 +36,6 @@ use phpOMS\Message\ResponseAbstract; */ final class ApiController extends Controller { - /** - * Api method to update a bill - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillUpdate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBill')->apiBillUpdate($request, $response, $data); - } - - /** - * Api method to create a bill - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBill')->apiBillCreate($request, $response, $data); - } - - /** - * Api method to create a bill - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiMediaAddToBill(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBill')->apiMediaAddToBill($request, $response, $data); - } - - /** - * Api method to create a bill element - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillElementCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBill')->apiBillElementCreate($request, $response, $data); - } - - /** - * Api method to create a bill preview - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiPreviewRender(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBill')->apiPreviewRender($request, $response, $data); - } - - /** - * Api method to create and archive a bill - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillPdfArchiveCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBill')->apiBillPdfArchiveCreate($request, $response, $data); - } - - /** - * Api method to create a bill - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillPdfCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBill')->apiBillPdfCreate($request, $response, $data); - } - - /** - * Api method to create bill files - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiNoteCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBill')->apiNoteCreate($request, $response, $data); - } - - /** - * Api method to create bill files - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiSupplierBillUpload(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiPurchase')->apiSupplierBillUpload($request, $response, $data); - } - - /** - * Api method to create item bill type - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillTypeCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBillType')->apiBillTypeCreate($request, $response, $data); - } - - /** - * Api method to create item attribute l11n - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillTypeL11nCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiBillType')->apiBillTypeL11nCreate($request, $response, $data); - } - - /** - * Api method to create item bill type - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiTaxCombinationCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiTax')->apiTaxCombinationCreate($request, $response, $data); - } - - /** - * Api method to create item bill type - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiPriceCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiPrice')->apiPriceCreate($request, $response, $data); - } - - /** - * Api method to create item attribute - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillAttributeCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiAttribute')->apiBillAttributeCreate($request, $response, $data); - } - - /** - * Api method to create bill attribute l11n - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillAttributeTypeL11nCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiAttribute')->apiBillAttributeTypeL11nCreate($request, $response, $data); - } - - /** - * Api method to create bill attribute type - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillAttributeTypeCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiAttribute')->apiBillAttributeTypeCreate($request, $response, $data); - } - - /** - * Api method to create bill attribute value - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillAttributeValueCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiAttribute')->apiBillAttributeValueCreate($request, $response, $data); - } - - /** - * Api method to create bill attribute l11n - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiBillAttributeValueL11nCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - $this->app->moduleManager->get('Billing', 'ApiAttribute')->apiBillAttributeValueL11nCreate($request, $response, $data); - } - - /** - * Api method to find subscriptions - * - * @param RequestAbstract $request Request - * @param ResponseAbstract $response Response - * @param array $data Generic data - * - * @return void - * - * @api - * - * @since 1.0.0 - */ - public function apiSubscriptionFind(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void - { - } - /** * Api method to create item payment type * @@ -436,8 +95,11 @@ final class ApiController extends Controller */ private function createPaymentTermFromRequest(RequestAbstract $request) : BaseStringL11nType { - $paymentTerm = new BaseStringL11nType($request->getDataString('name') ?? ''); - $paymentTerm->setL11n($request->getDataString('title') ?? '', $request->getDataString('language') ?? ISO639x1Enum::_EN); + $paymentTerm = new BaseStringL11nType($request->getDataString('name') ?? ''); + $paymentTerm->setL11n( + $request->getDataString('title') ?? '', + ISO639x1Enum::tryFromValue($request->getDataString('language')) ?? ISO639x1Enum::_EN + ); return $paymentTerm; } @@ -501,12 +163,10 @@ final class ApiController extends Controller */ private function createPaymentTermL11nFromRequest(RequestAbstract $request) : BaseStringL11n { - $paymentL11n = new BaseStringL11n(); - $paymentL11n->ref = $request->getDataInt('type') ?? 0; - $paymentL11n->setLanguage( - $request->getDataString('language') ?? $request->header->l11n->language - ); - $paymentL11n->content = $request->getDataString('title') ?? ''; + $paymentL11n = new BaseStringL11n(); + $paymentL11n->ref = $request->getDataInt('type') ?? 0; + $paymentL11n->language = ISO639x1Enum::tryFromValue($request->getDataString('language')) ?? $request->header->l11n->language; + $paymentL11n->content = $request->getDataString('title') ?? ''; return $paymentL11n; } @@ -570,8 +230,11 @@ final class ApiController extends Controller */ private function createShippingTermFromRequest(RequestAbstract $request) : BaseStringL11nType { - $shippingTerm = new BaseStringL11nType($request->getDataString('name') ?? ''); - $shippingTerm->setL11n($request->getDataString('title') ?? '', $request->getDataString('language') ?? ISO639x1Enum::_EN); + $shippingTerm = new BaseStringL11nType($request->getDataString('name') ?? ''); + $shippingTerm->setL11n( + $request->getDataString('title') ?? '', + ISO639x1Enum::tryFromValue($request->getDataString('language')) ?? ISO639x1Enum::_EN + ); return $shippingTerm; } @@ -635,12 +298,10 @@ final class ApiController extends Controller */ private function createShippingTermL11nFromRequest(RequestAbstract $request) : BaseStringL11n { - $shippingL11n = new BaseStringL11n(); - $shippingL11n->ref = $request->getDataInt('type') ?? 0; - $shippingL11n->setLanguage( - $request->getDataString('language') ?? $request->header->l11n->language - ); - $shippingL11n->content = $request->getDataString('title') ?? ''; + $shippingL11n = new BaseStringL11n(); + $shippingL11n->ref = $request->getDataInt('type') ?? 0; + $shippingL11n->language = ISO639x1Enum::tryFromValue($request->getDataString('language')) ?? $request->header->l11n->language; + $shippingL11n->content = $request->getDataString('title') ?? ''; return $shippingL11n; } diff --git a/Controller/ApiPriceController.php b/Controller/ApiPriceController.php index 4945162..6773af8 100755 --- a/Controller/ApiPriceController.php +++ b/Controller/ApiPriceController.php @@ -16,15 +16,20 @@ declare(strict_types=1); namespace Modules\Billing\Controller; use Modules\Attribute\Models\NullAttributeValue; +use Modules\Billing\Models\Price\NullPrice; use Modules\Billing\Models\Price\Price; use Modules\Billing\Models\Price\PriceMapper; use Modules\Billing\Models\Price\PriceType; use Modules\Billing\Models\Tax\TaxCombinationMapper; +use Modules\ClientManagement\Models\Client; use Modules\ClientManagement\Models\ClientMapper; use Modules\ClientManagement\Models\NullClient; +use Modules\Finance\Models\TaxCodeMapper; +use Modules\ItemManagement\Models\Item; use Modules\ItemManagement\Models\ItemMapper; use Modules\ItemManagement\Models\NullItem; use Modules\SupplierManagement\Models\NullSupplier; +use Modules\SupplierManagement\Models\Supplier; use Modules\SupplierManagement\Models\SupplierMapper; use phpOMS\Localization\ISO4217CharEnum; use phpOMS\Message\Http\RequestStatusCode; @@ -43,6 +48,177 @@ use phpOMS\System\MimeType; */ final class ApiPriceController extends Controller { + public function findBestPrice(RequestAbstract $request, ?Item $item = null, ?Client $client = null, ?Supplier $supplier = null) + { + // Get item + if ($item === null && $request->hasData('price_item')) { + /** @var null|\Modules\ItemManagement\Models\Item $item */ + $item = ItemMapper::get() + ->with('attributes') + ->with('attributes/type') + ->with('attributes/value') + ->where('id', (int) $request->getData('price_item')) + ->where('attributes/type/name', ['segment', 'section', 'sales_group', 'product_group', 'product_type'], 'IN') + ->execute(); + } + + // Get client + if ($client === null && $request->hasData('client')) { + /** @var \Modules\ClientManagement\Models\Client $client */ + $client = ClientMapper::get() + ->with('attributes') + ->with('attributes/type') + ->with('attributes/value') + ->where('id', (int) $request->getData('client')) + ->where('attributes/type/name', ['segment', 'section', 'client_group', 'client_type'], 'IN') + ->execute(); + } + + $quantity = new FloatInt($request->getDataString('price_quantity') ?? 10000); + + // Get all relevant prices + $queryMapper = PriceMapper::getAll(); + + if ($request->hasData('price_name')) { + $queryMapper->where('name', $request->getData('name')); + } + + $queryMapper->where('promocode', \array_unique([$request->getData('promocode'), null]), 'IN'); + + $queryMapper->where('item', \array_unique([$request->getDataInt('item'), $item?->id, null]), 'IN'); + $queryMapper->where('itemsalesgroup', \array_unique([$request->getDataInt('sales_group'), $item?->getAttribute('sales_group')->value->getValue(), null]), 'IN'); + $queryMapper->where('itemproductgroup', \array_unique([$request->getDataInt('product_group'), $item?->getAttribute('product_group')->value->getValue(), null]), 'IN'); + $queryMapper->where('itemsegment', \array_unique([$request->getDataInt('item_segment'), $item?->getAttribute('segment')->value->getValue(), null]), 'IN'); + $queryMapper->where('itemsection', \array_unique([$request->getDataInt('item_section'), $item?->getAttribute('section')->value->getValue(), null]), 'IN'); + $queryMapper->where('itemtype', \array_unique([$request->getDataInt('product_type'), $item?->getAttribute('product_type')->value->getValue(), null]), 'IN'); + + $queryMapper->where('client', \array_unique([$request->getDataInt('client'), $client?->id, null]), 'IN'); + $queryMapper->where('clientgroup', \array_unique([$request->getDataInt('client_group'), $client?->getAttribute('client_group')->value->getValue(), null]), 'IN'); + $queryMapper->where('clientsegment', \array_unique([$request->getDataInt('client_segment'), $client?->getAttribute('segment')->value->getValue(), null]), 'IN'); + $queryMapper->where('clientsection', \array_unique([$request->getDataInt('client_section'), $client?->getAttribute('section')->value->getValue(), null]), 'IN'); + $queryMapper->where('clienttype', \array_unique([$request->getDataInt('client_type'), $client?->getAttribute('client_type')->value->getValue(), null]), 'IN'); + $queryMapper->where('clientcountry', \array_unique([$request->getData('client_region'), $client?->mainAddress->country, null]), 'IN'); + + $queryMapper->where('supplier', \array_unique([$request->getDataInt('supplier'), $supplier?->id, null]), 'IN'); + $queryMapper->where('unit', \array_unique([$request->getDataInt('price_unit'), null]), 'IN'); + $queryMapper->where('type', $request->getDataInt('price_type') ?? PriceType::SALES); + $queryMapper->where('currency', \array_unique([$request->getDataString('currency'), null]), 'IN'); + + // @todo implement start and end + + /* + @todo implement quantity + if ($request->hasData('price_quantity')) { + $whereQuery = new Where(); + $whereQuery->where('quantity', (int) $request->getData('price_quantity'), '<=') + ->where('quantity', null, '=', 'OR') + + $queryMapper->where('quantity', $whereQuery); + } + */ + + /** @var \Modules\Billing\Models\Price\Price[] $prices */ + $prices = $queryMapper->execute(); + + // Find base price (@todo probably not a good solution) + $basePrice = null; + foreach ($prices as $price) { + if ($price->priceNew > 0 + && $price->item->id !== 0 + && $price->itemsalesgroup->id === 0 + && $price->itemproductgroup->id === 0 + && $price->itemsegment->id === 0 + && $price->itemsection->id === 0 + && $price->itemtype->id === 0 + && $price->client->id === 0 + && $price->clientgroup->id === 0 + && $price->clientsegment->id === 0 + && $price->clientsection->id === 0 + && $price->clienttype->id === 0 + && $price->promocode === '' + && $price->priceNew->value < ($basePrice?->priceNew->value ?? \PHP_INT_MAX) + ) { + $basePrice = $price; + } + } + + $basePrice ??= new NullPrice(); + + // @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 'alwaysimproces' which always overwrites other prices + // Find best price + $bestPrice = $basePrice; + $bestPriceValue = \PHP_INT_MAX; + + $discounts = []; + + foreach ($prices as $price) { + if ($price->isAdditive && $price->priceNew->value === 0) { + $discounts[] = $price; + } + + $newPrice = $bestPrice->price->value ?? $basePrice->price->value; + + if ($price->priceNew->value > 0 && $price->priceNew->value < $newPrice) { + $newPrice = $price->priceNew->value; + } + + if ($price->priceNew->value > 0 && $price->priceNew->value < $newPrice) { + $newPrice = $price->priceNew->value; + } + + // Calculate the price EFFECT (this is the theoretical unit price) + // 1. subtract discount value + // 2. subtract discount percentage + // 3. subtract bonus effect + + $newPrice -= $price->discount->value; + $newPrice = (int) ($newPrice - $price->bonus->value / 10000 * $price->priceNew->value / $quantity->value); + $newPrice = (int) ((1000000 - $price->discountPercentage->value) / 1000000 * $newPrice); + + // @todo If a customer receives 1+1 but purchases 2, then he gets 2+2 (if multiply === true) which is better than 1+1 with multiply false. + // Same goes for amount discounts? + + if ($newPrice < $bestPriceValue) { + $bestPriceValue = $newPrice; + $bestPrice = $price; + } + } + + if ($bestPrice->price->value === 0) { + $discounts[] = clone $bestPrice; + $bestPrice = $basePrice; + } + + // Actual price calculation + $bestActualPriceValue = $bestPrice?->price->value ?? \PHP_INT_MAX; + + $discountAmount = $bestPrice->discount->value; + $discountPercentage = $bestPrice->discountPercentage->value; + $bonus = $bestPrice->bonus->value; + + foreach ($discounts as $discount) { + $bestActualPriceValue -= $discount->discount->value; + + $discountAmount += $discount->discount->value; + $discountPercentage += $discount->discountPercentage->value; + $bonus += $discount->bonus->value; + } + + $bestActualPriceValue -= $discountAmount; + $bestActualPriceValue = (int) \round((1000000 - $discountPercentage) / 1000000 * $bestActualPriceValue, 0); + + return [ + 'basePrice' => $basePrice->price, + 'bestPrice' => $bestPrice->price, + 'bestActualPrice' => new FloatInt($bestActualPriceValue), + 'discounts' => $discounts, + 'discountPercent' => new FloatInt($discountPercentage), + 'discountAmount' => new FloatInt($discountAmount), + 'bonus' => new FloatInt($bonus), + ]; + } + /** * Api method to find items * @@ -74,13 +250,6 @@ final class ApiPriceController extends Controller // Get account /** @var null|\Modules\ClientManagement\Models\Client|\Modules\SupplierManagement\Models\Supplier $account */ $account = null; - - /** @var null|\Modules\ClientManagement\Models\Client $client */ - $client = null; - - /** @var null|\Modules\SupplierManagement\Models\Supplier $supplier */ - $supplier = null; - if ($request->hasData('client')) { /** @var \Modules\ClientManagement\Models\Client $client */ $client = ClientMapper::get() @@ -105,6 +274,8 @@ final class ApiPriceController extends Controller $account = $supplier; } + $quantity = new FloatInt($request->getDataString('price_quantity') ?? 10000); + // Get all relevant prices $queryMapper = PriceMapper::getAll(); @@ -126,7 +297,7 @@ final class ApiPriceController extends Controller $queryMapper->where('clientsegment', \array_unique([$request->getData('client_segment', 'int'), $client?->getAttribute('segment')->id, null]), 'IN'); $queryMapper->where('clientsection', \array_unique([$request->getData('client_section', 'int'), $client?->getAttribute('section')->id, null]), 'IN'); $queryMapper->where('clienttype', \array_unique([$request->getData('client_type', 'int'), $client?->getAttribute('client_type')->id, null]), 'IN'); - $queryMapper->where('clientcountry', \array_unique([$request->getData('client_region'), $client?->mainAddress->getCountry(), null]), 'IN'); + $queryMapper->where('clientcountry', \array_unique([$request->getData('client_region'), $client?->mainAddress->country, null]), 'IN'); $queryMapper->where('supplier', \array_unique([$request->getData('supplier', 'int'), null]), 'IN'); $queryMapper->where('unit', \array_unique([$request->getData('price_unit', 'int'), null]), 'IN'); @@ -152,7 +323,7 @@ final class ApiPriceController extends Controller // Find base price (@todo probably not a good solution) $bestBasePrice = null; foreach ($prices as $price) { - if ($price->price->value !== 0 && $price->priceNew === 0 + if ($price->priceNew->value !== 0 && $price->priceNew === 0 && $price->item->id !== 0 && $price->itemsalesgroup->id === 0 && $price->itemproductgroup->id === 0 @@ -165,7 +336,7 @@ final class ApiPriceController extends Controller && $price->clientsection->id === 0 && $price->clienttype->id === 0 && $price->promocode === '' - && $price->price->value < ($bestBasePrice?->price->value ?? \PHP_INT_MAX) + && $price->priceNew->value < ($bestBasePrice?->price->value ?? \PHP_INT_MAX) ) { $bestBasePrice = $price; } @@ -180,20 +351,21 @@ final class ApiPriceController extends Controller foreach ($prices as $price) { $newPrice = $bestBasePrice?->price->value ?? \PHP_INT_MAX; - if ($price->price->value < $newPrice) { - $newPrice = $price->price->value; + if ($price->priceNew->value < $newPrice) { + $newPrice = $price->priceNew->value; } - if ($price->priceNew < $newPrice) { - $newPrice = $price->priceNew; - } + // Calculate the price EFFECT (this is the theoretical unit price) + // 1. subtract discount value + // 2. subtract discount percentage + // 3. subtract bonus effect - $newPrice -= $price->discount; - $newPrice = (int) ((10000 / $price->discountPercentage) * $newPrice); - $newPrice = (int) (($price->quantity === 0 ? 10000 : $price->quantity) / (10000 + $price->bonus) * $newPrice); + $newPrice -= $price->discount->value; + $newPrice = (int) ((1000000 - $price->discountPercentage->value) / 1000000 * $newPrice); + $newPrice = (int) ($newPrice - $price->bonus->value / 10000 * $price->priceNew->value / $quantity->value); - // @todo the calculation above regarding discount and bonus don't consider the purchased quantity. - // If a customer receives 1+1 but purchases 2, then he gets 2+2 (if multiply === true) which is better than 1+1 with multiply false. + // @todo If a customer receives 1+1 but purchases 2, then he gets 2+2 (if multiply === true) which is better than 1+1 with multiply false. + // Same goes for amount discounts? if ($newPrice < $bestPriceValue) { $bestPriceValue = $newPrice; @@ -201,6 +373,12 @@ final class ApiPriceController extends Controller } } + // Actual price calculation + $bestActualPrice = $bestBasePrice?->price->value ?? \PHP_INT_MAX; + $bestActualPrice -= $bestPrice->discount->value; + + // @todo now perform subtractive improvements (e.g. promocodes are often subtractive) + // Get tax definition /** @var \Modules\Billing\Models\Tax\TaxCombination $tax */ $tax = ($request->getDataInt('price_type') ?? PriceType::SALES) === PriceType::SALES @@ -213,10 +391,22 @@ final class ApiPriceController extends Controller ->where('supplierCode', $account->getAttribute('supplier_code')->value->id) ->execute(); + $taxCode = TaxCodeMapper::get() + ->where('abbr', $tax->taxCode) + ->execute(); + + $result = [ + 'taxcode' => $taxCode->abbr, + 'grossPercentage' => $taxCode->percentageInvoice, + 'net' => $bestActualPrice, + 'taxes' => $bestActualPrice * $taxCode->percentageInvoice / 1000000, + 'gross' => $bestActualPrice + $bestActualPrice * $taxCode->percentageInvoice / 1000000, + ]; + $response->header->set('Content-Type', MimeType::M_JSON, true); $response->set( $request->uri->__toString(), - \array_values($prices) + $result ); } @@ -262,12 +452,12 @@ final class ApiPriceController extends Controller $price->name = $request->getDataString('name') ?? ''; $price->promocode = $request->getDataString('promocode') ?? ''; - $price->item = new NullItem((int) $request->getData('item')); - $price->itemsegment = new NullAttributeValue((int) $request->getData('itemsegment')); - $price->itemsection = new NullAttributeValue((int) $request->getData('itemsection')); + $price->item = new NullItem((int) $request->getData('item')); + $price->itemsegment = new NullAttributeValue((int) $request->getData('itemsegment')); + $price->itemsection = new NullAttributeValue((int) $request->getData('itemsection')); $price->itemsalesgroup = new NullAttributeValue((int) $request->getData('itemsalesgroup')); - $price->itemproductgroup = new NullAttributeValue((int) $request->getData('itemproductgroup')); - $price->itemtype = new NullAttributeValue((int) $request->getData('itemtype')); + $price->itemproductgroup = new NullAttributeValue((int) $request->getData('itemproductgroup')); + $price->itemtype = new NullAttributeValue((int) $request->getData('itemtype')); $price->client = new NullClient((int) $request->getData('client')); $price->clientsegment = new NullAttributeValue((int) $request->getData('clientsegment')); @@ -277,15 +467,15 @@ final class ApiPriceController extends Controller $price->supplier = new NullSupplier((int) $request->getData('supplier')); $price->unit = (int) $request->getData('unit'); - $price->type = $request->getDataInt('type') ?? PriceType::SALES; - $price->quantity = (int) $request->getData('quantity'); - $price->price = new FloatInt((int) $request->getData('price')); - $price->priceNew = (int) $request->getData('price_new'); - $price->discount = (int) $request->getData('discount'); - $price->discountPercentage = (int) $request->getData('discountPercentage'); - $price->bonus = (int) $request->getData('bonus'); + $price->type = PriceType::tryFromValue($request->getDataInt('type')) ?? PriceType::SALES; + $price->quantity = new FloatInt($request->getDataString('quantity') ?? 0); + $price->price = new FloatInt($request->getDataString('price') ?? 0); + $price->priceNew = new FloatInt($request->getDataString('price_new') ?? 0); + $price->discount = new FloatInt($request->getDataString('discount') ?? 0); + $price->discountPercentage = new FloatInt($request->getDataString('discountPercentage') ?? 0); + $price->bonus = new FloatInt($request->getDataString('bonus') ?? 0); $price->multiply = $request->getDataBool('multiply') ?? false; - $price->currency = $request->getDataString('currency') ?? ISO4217CharEnum::_EUR; + $price->currency = ISO4217CharEnum::tryFromValue($request->getDataString('currency')) ?? ISO4217CharEnum::_EUR; $price->start = $request->getDataDateTime('start'); $price->end = $request->getDataDateTime('end'); @@ -299,7 +489,7 @@ final class ApiPriceController extends Controller * * @return array * - * @todo consider to prevent name 'base'? + * @todo consider to prevent name 'default'? * Might not be possible because it is used internally as well (see apiItemCreate in ItemManagement) * * @since 1.0.0 @@ -356,18 +546,18 @@ final class ApiPriceController extends Controller */ public function updatePriceFromRequest(RequestAbstract $request, Price $new) : Price { - $new->name = $new->name !== 'base' + $new->name = $new->name !== 'default' ? ($request->getDataString('name') ?? $new->name) : $new->name; $new->promocode = $request->getDataString('promocode') ?? $new->promocode; - $new->item = $request->hasData('item') ? new NullItem((int) $request->getData('item')) : $new->item; + $new->item = $request->hasData('item') ? new NullItem((int) $request->getData('item')) : $new->item; $new->itemsalesgroup = $request->hasData('itemsalesgroup') ? new NullAttributeValue((int) $request->getData('itemsalesgroup')) : $new->itemsalesgroup; - $new->itemproductgroup = $request->hasData('itemproductgroup') ? new NullAttributeValue((int) $request->getData('itemproductgroup')) : $new->itemproductgroup; - $new->itemsegment = $request->hasData('itemsegment') ? new NullAttributeValue((int) $request->getData('itemsegment')) : $new->itemsegment; - $new->itemsection = $request->hasData('itemsection') ? new NullAttributeValue((int) $request->getData('itemsection')) : $new->itemsection; - $new->itemtype = $request->hasData('itemtype') ? new NullAttributeValue((int) $request->getData('itemtype')) : $new->itemtype; + $new->itemproductgroup = $request->hasData('itemproductgroup') ? new NullAttributeValue((int) $request->getData('itemproductgroup')) : $new->itemproductgroup; + $new->itemsegment = $request->hasData('itemsegment') ? new NullAttributeValue((int) $request->getData('itemsegment')) : $new->itemsegment; + $new->itemsection = $request->hasData('itemsection') ? new NullAttributeValue((int) $request->getData('itemsection')) : $new->itemsection; + $new->itemtype = $request->hasData('itemtype') ? new NullAttributeValue((int) $request->getData('itemtype')) : $new->itemtype; $new->client = $request->hasData('client') ? new NullClient((int) $request->getData('client')) : $new->client; $new->clientgroup = $request->hasData('clientgroup') ? new NullAttributeValue((int) $request->getData('clientgroup')) : $new->clientgroup; @@ -377,7 +567,7 @@ final class ApiPriceController extends Controller $new->supplier = $request->hasData('supplier') ? new NullSupplier((int) $request->getData('supplier')) : $new->supplier; $new->unit = $request->getDataInt('unit') ?? $new->unit; - $new->type = $request->getDataInt('type') ?? $new->type; + $new->type = PriceType::tryFromValue($request->getDataInt('type')) ?? $new->type; $new->quantity = $request->getDataInt('quantity') ?? $new->quantity; $new->price = $request->hasData('price') ? new FloatInt((int) $request->getData('price')) : $new->price; $new->priceNew = $request->getDataInt('price_new') ?? $new->priceNew; @@ -400,7 +590,7 @@ final class ApiPriceController extends Controller * @return array * * @todo implement - * @todo consider to block 'base' name + * @todo consider to block 'default' name * * @since 1.0.0 */ @@ -439,7 +629,7 @@ final class ApiPriceController extends Controller /** @var \Modules\Billing\Models\Price\Price $price */ $price = PriceMapper::get()->where('id', (int) $request->getData('id'))->execute(); - if ($price->name === 'base') { + if ($price->name === 'default') { // default price cannot be deleted $this->createInvalidDeleteResponse($request, $response, []); diff --git a/Controller/ApiPurchaseController.php b/Controller/ApiPurchaseController.php index 0c9e38a..2fb5c43 100755 --- a/Controller/ApiPurchaseController.php +++ b/Controller/ApiPurchaseController.php @@ -27,7 +27,6 @@ use phpOMS\Message\ResponseAbstract; use phpOMS\System\OperatingSystem; use phpOMS\System\SystemType; use phpOMS\System\SystemUtils; -use phpOMS\Uri\HttpUri; /** * Billing class. @@ -73,7 +72,7 @@ final class ApiPurchaseController extends Controller $files = $request->files; foreach ($files as $file) { // Create default bill - $billRequest = new HttpRequest(new HttpUri('')); + $billRequest = new HttpRequest(); $billRequest->header->account = $request->header->account; $billRequest->header->l11n = $request->header->l11n; $billRequest->setData('supplier', 0); @@ -83,7 +82,7 @@ final class ApiPurchaseController extends Controller $billResponse = new HttpResponse(); $billResponse->header->l11n = $response->header->l11n; - $this->app->moduleManager->get('Billing', 'Api')->apiBillCreate($billRequest, $billResponse, $data); + $this->app->moduleManager->get('Billing', 'ApiBill')->apiBillCreate($billRequest, $billResponse, $data); $billId = $billResponse->getDataArray('')['response']->id; @@ -99,7 +98,7 @@ final class ApiPurchaseController extends Controller $mediaRequest->setData('bill', $billId); $mediaRequest->setData('type', $originalType); $mediaRequest->setData('parse_content', true, true); - $this->app->moduleManager->get('Billing', 'Api')->apiMediaAddToBill($mediaRequest, $mediaResponse, $data); + $this->app->moduleManager->get('Billing', 'ApiBill')->apiMediaAddToBill($mediaRequest, $mediaResponse, $data); /** @var \Modules\Media\Models\Media[] $uploaded */ $uploaded = $mediaResponse->getDataArray('')['response']['upload']; @@ -116,12 +115,12 @@ final class ApiPurchaseController extends Controller // Create internal document $billResponse = new HttpResponse(); - $billRequest = new HttpRequest(new HttpUri('')); + $billRequest = new HttpRequest(); $billRequest->header->account = $request->header->account; $billRequest->setData('bill', $billId); - $this->app->moduleManager->get('Billing', 'Api')->apiBillPdfArchiveCreate($billRequest, $billResponse); + $this->app->moduleManager->get('Billing', 'ApiBill')->apiBillPdfArchiveCreate($billRequest, $billResponse); // Offload bill parsing to cli $cliPath = \realpath(__DIR__ . '/../../../cli.php'); diff --git a/Controller/ApiTaxController.php b/Controller/ApiTaxController.php index 54d5f7f..c0bf9a7 100755 --- a/Controller/ApiTaxController.php +++ b/Controller/ApiTaxController.php @@ -20,11 +20,11 @@ use Modules\Billing\Models\Tax\TaxCombination; use Modules\Billing\Models\Tax\TaxCombinationMapper; use Modules\ClientManagement\Models\Attribute\ClientAttributeTypeMapper; use Modules\ClientManagement\Models\Client; -use Modules\Finance\Models\TaxCode; -use Modules\Finance\Models\TaxCodeMapper; use Modules\ItemManagement\Models\Item; use Modules\Organization\Models\UnitMapper; -use phpOMS\Localization\ISO3166CharEnum; +use Modules\SupplierManagement\Models\Attribute\SupplierAttributeTypeMapper; +use Modules\SupplierManagement\Models\Supplier; +use phpOMS\Localization\ISO3166TwoEnum; use phpOMS\Message\Http\RequestStatusCode; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; @@ -48,28 +48,35 @@ final class ApiTaxController extends Controller * @param Item $item Item to get tax code from * @param string $defaultCountry default country to use if no valid tax code could be found and if the unit country code shouldn't be used * - * @return TaxCode + * @return TaxCombination * * @since 1.0.0 */ - public function getTaxCodeFromClientItem(Client $client, Item $item, string $defaultCountry = '') : TaxCode + public function getTaxForPerson(?Client $client = null, ?Supplier $supplier = null, Item $item, string $defaultCountry = '') : TaxCombination { // @todo define default sales tax code if none available?! - // @todo consider to actually use a ownsOne reference instead of only a string, this way the next line with the TaxCodeMapper can be removed + $itemCode = 0; + $accountCode = 0; + $combinationType = 'clientCode'; + + if ($client !== null) { + $itemCode = $item->getAttribute('sales_tax_code')->value->id; + $accountCode = $client->getAttribute('sales_tax_code')->value->id; + } else { + $itemCode = $item->getAttribute('purchase_tax_code')->value->id; + $accountCode = $supplier->getAttribute('purchase_tax_code')->value->id; + $combinationType = 'supplierCode'; + } /** @var \Modules\Billing\Models\Tax\TaxCombination $taxCombination */ $taxCombination = TaxCombinationMapper::get() - ->where('itemCode', $item->getAttribute('sales_tax_code')->value->id) - ->where('clientCode', $client->getAttribute('sales_tax_code')->value->id) + ->with('taxCode') + ->where('itemCode', $itemCode) + ->where($combinationType, $accountCode) ->execute(); - /** @var \Modules\Finance\Models\TaxCode $taxCode */ - $taxCode = TaxCodeMapper::get() - ->where('abbr', $taxCombination->taxCode) - ->execute(); - - if ($taxCode->id !== 0) { - return $taxCode; + if ($taxCombination->taxCode->id !== 0) { + return $taxCombination; } /** @var \Modules\Organization\Models\Unit $unit */ @@ -78,28 +85,23 @@ final class ApiTaxController extends Controller ->where('id', $this->app->unitId) ->execute(); - // Create dummy client - $client = new Client(); - $client->mainAddress = $unit->mainAddress; + // Create dummy + $account = $client !== null ? new Client() : new Supplier(); + $account->mainAddress = $unit->mainAddress; if (!empty($defaultCountry)) { - $client->mainAddress->setCountry($defaultCountry); + $account->mainAddress->setCountry($defaultCountry); } - $taxCodeAttribute = $this->getClientTaxCode($client, $unit->mainAddress); + $taxCodeAttribute = $client !== null + ? $this->getClientTaxCode($account, $unit->mainAddress) + : $this->getSupplierTaxCode($account, $unit->mainAddress); - /** @var \Modules\Billing\Models\Tax\TaxCombination $taxCombination */ - $taxCombination = TaxCombinationMapper::get() - ->where('itemCode', $item->getAttribute('sales_tax_code')->value->id) - ->where('clientCode', $taxCodeAttribute->id) + return TaxCombinationMapper::get() + ->with('taxCode') + ->where('itemCode', $itemCode) + ->where($combinationType, $taxCodeAttribute->id) ->execute(); - - /** @var \Modules\Finance\Models\TaxCode $taxCode */ - $taxCode = TaxCodeMapper::get() - ->where('abbr', $taxCombination->taxCode) - ->execute(); - - return $taxCode; } /** @@ -138,10 +140,10 @@ final class ApiTaxController extends Controller */ private function createTaxCombinationFromRequest(RequestAbstract $request) : TaxCombination { - $tax = new TaxCombination(); - $tax->taxType = $request->getDataInt('tax_type') ?? 1; - $tax->taxCode = (string) $request->getData('tax_code'); - $tax->itemCode = new NullAttributeValue((int) $request->getData('item_code')); + $tax = new TaxCombination(); + $tax->taxType = $request->getDataInt('tax_type') ?? 1; + $tax->taxCode->abbr = (string) $request->getData('tax_code'); + $tax->itemCode = new NullAttributeValue((int) $request->getData('item_code')); if ($tax->taxType === 1) { $tax->clientCode = new NullAttributeValue((int) $request->getData('account_code')); @@ -196,24 +198,72 @@ final class ApiTaxController extends Controller $taxCode = new NullAttributeValue(); // @todo need to consider own tax id as well - if ($taxOfficeAddress->getCountry() === $client->mainAddress->getCountry()) { - $taxCode = $codes->getDefaultByValue($client->mainAddress->getCountry()); - } elseif (\in_array($taxOfficeAddress->getCountry(), ISO3166CharEnum::getRegion('eu')) - && \in_array($client->mainAddress->getCountry(), ISO3166CharEnum::getRegion('eu')) + // @todo consider delivery & invoice location (Reihengeschaeft) + if ($taxOfficeAddress->country === $client->mainAddress->country) { + // Same country as we (= local tax code) + return $codes->getDefaultByValue($client->mainAddress->country); + } elseif (\in_array($taxOfficeAddress->country, ISO3166TwoEnum::getRegion('eu')) + && \in_array($client->mainAddress->country, ISO3166TwoEnum::getRegion('eu')) ) { if (!empty($client->getAttribute('vat_id')->value->getValue())) { - // Is EU company - $taxCode = $codes->getDefaultByValue('EU'); + // Is EU company and we are EU company + return $codes->getDefaultByValue('EU'); } else { - // Is EU private customer - $taxCode = $codes->getDefaultByValue($client->mainAddress->getCountry()); + // Is EU private customer and we are EU company + return $codes->getDefaultByValue($client->mainAddress->country); } - } elseif (\in_array($taxOfficeAddress->getCountry(), ISO3166CharEnum::getRegion('eu'))) { + } elseif (\in_array($taxOfficeAddress->country, ISO3166TwoEnum::getRegion('eu'))) { // None EU company but we are EU company - $taxCode = $codes->getDefaultByValue('INT'); + return $codes->getDefaultByValue('INT'); } else { // None EU company and we are also none EU company - $taxCode = $codes->getDefaultByValue('INT'); + return $codes->getDefaultByValue('INT'); + } + + return $taxCode; + } + + /** + * Get the client's tax code based on their country and tax office address + * + * @param Supplier $client The client to get the tax code for + * @param Address $taxOfficeAddress The tax office address used to determine the tax code + * + * @return AttributeValue The client's tax code + * + * @since 1.0.0 + */ + public function getSupplierTaxCode(Supplier $client, Address $taxOfficeAddress) : AttributeValue + { + /** @var \Modules\Attribute\Models\AttributeType $codes */ + $codes = SupplierAttributeTypeMapper::get() + ->with('defaults') + ->where('name', 'purchase_tax_code') + ->execute(); + + $taxCode = new NullAttributeValue(); + + // @todo need to consider own tax id as well + // @todo consider delivery & invoice location (Reihengeschaeft) + if ($taxOfficeAddress->country === $client->mainAddress->country) { + // Same country as we (= local tax code) + return $codes->getDefaultByValue($client->mainAddress->country); + } elseif (\in_array($taxOfficeAddress->country, ISO3166TwoEnum::getRegion('eu')) + && \in_array($client->mainAddress->country, ISO3166TwoEnum::getRegion('eu')) + ) { + if (!empty($client->getAttribute('vat_id')->value->getValue())) { + // Is EU company and we are EU company + return $codes->getDefaultByValue('EU'); + } else { + // Is EU private customer and we are EU company + return $codes->getDefaultByValue($client->mainAddress->country); + } + } elseif (\in_array($taxOfficeAddress->country, ISO3166TwoEnum::getRegion('eu'))) { + // None EU company but we are EU company + return $codes->getDefaultByValue('INT'); + } else { + // None EU company and we are also none EU company + return $codes->getDefaultByValue('INT'); } return $taxCode; diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 9dadae6..c2fe532 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -113,6 +113,7 @@ final class BackendController extends Controller $bill = SalesBillMapper::get() ->with('client') ->with('elements') + ->with('elements/container') ->with('files') ->with('files/types') ->with('notes') @@ -144,14 +145,12 @@ final class BackendController extends Controller ->with('createdBy') ->where('module', 'Billing') ->where('type', StringUtils::intHash(BillElementMapper::class)) - ->where('ref', \array_keys($bill->getElements()), 'IN') + ->where('ref', \array_keys($bill->elements), 'IN') ->execute(); $logs = \array_merge($logs, $logsElements); } - $logs = \array_merge($logs, $logsElements); - $view->data['logs'] = $logs; $view->data['media-upload'] = new \Modules\Media\Theme\Backend\Components\Upload\BaseView($this->app->l11nManager, $request, $response); @@ -298,7 +297,9 @@ final class BackendController extends Controller $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1005105001, $request, $response); $bill = PurchaseBillMapper::get() + ->with('supplier') ->with('elements') + ->with('elements/container') ->with('files') ->with('files/types') ->with('notes') @@ -338,7 +339,7 @@ final class BackendController extends Controller ->with('createdBy') ->where('module', 'Billing') ->where('type', StringUtils::intHash(BillElementMapper::class)) - ->where('ref', \array_keys($bill->getElements()), 'IN') + ->where('ref', \array_keys($bill->elements), 'IN') ->execute(); $logs = \array_merge($logs, $logsElements); diff --git a/Controller/CliController.php b/Controller/CliController.php index 9818ab8..2d1ff63 100755 --- a/Controller/CliController.php +++ b/Controller/CliController.php @@ -63,7 +63,7 @@ final class CliController extends Controller /** @var \Modules\Billing\Models\Bill $bill */ $bill = BillMapper::get() - ->with('media') + ->with('files') ->with('media/types') ->with('media/content') ->where('id', (int) $request->getData('i')) @@ -97,13 +97,13 @@ final class CliController extends Controller $supplierId = $this->matchSupplier($content, $suppliers); $bill->supplier = new NullSupplier($supplierId); - $supplier = $suppliers[$supplierId] ?? new NullSupplier(); + $supplier = $suppliers[$supplierId] ?? new NullSupplier(); $bill->billTo = $supplier->account->name1; $bill->billAddress = $supplier->mainAddress->address; $bill->billCity = $supplier->mainAddress->city; $bill->billZip = $supplier->mainAddress->postal; - $bill->billCountry = $supplier->mainAddress->getCountry(); + $bill->billCountry = $supplier->mainAddress->country; /* Type */ $type = $this->findSupplierInvoiceType($content, $identifiers['type'], $language); @@ -150,6 +150,8 @@ final class CliController extends Controller $view->setTemplate('/Modules/Billing/Theme/Cli/bill-parsed'); $view->data['bill'] = $bill; + // @todo change tax code during/after bill parsing + return $view; } diff --git a/Models/Attribute/BillAttributeTypeMapper.php b/Models/Attribute/BillAttributeTypeMapper.php index 77422d9..3429716 100755 --- a/Models/Attribute/BillAttributeTypeMapper.php +++ b/Models/Attribute/BillAttributeTypeMapper.php @@ -42,7 +42,8 @@ final class BillAttributeTypeMapper extends DataMapperFactory 'billing_attr_type_datatype' => ['name' => 'billing_attr_type_datatype', 'type' => 'int', 'internal' => 'datatype'], 'billing_attr_type_fields' => ['name' => 'billing_attr_type_fields', 'type' => 'int', 'internal' => 'fields'], 'billing_attr_type_custom' => ['name' => 'billing_attr_type_custom', 'type' => 'bool', 'internal' => 'custom'], - 'billing_attr_type_repeatable' => ['name' => 'billing_attr_type_repeatable', 'type' => 'bool', 'internal' => 'repeatable'], + 'billing_attr_type_repeatable' => ['name' => 'billing_attr_type_repeatable', 'type' => 'bool', 'internal' => 'repeatable'], + 'billing_attr_type_internal' => ['name' => 'billing_attr_type_internal', 'type' => 'bool', 'internal' => 'isInternal'], 'billing_attr_type_pattern' => ['name' => 'billing_attr_type_pattern', 'type' => 'string', 'internal' => 'validationPattern'], 'billing_attr_type_required' => ['name' => 'billing_attr_type_required', 'type' => 'bool', 'internal' => 'isRequired'], ]; diff --git a/Models/Attribute/BillAttributeValueL11nMapper.php b/Models/Attribute/BillAttributeValueL11nMapper.php index ade8ec8..939cd7e 100755 --- a/Models/Attribute/BillAttributeValueL11nMapper.php +++ b/Models/Attribute/BillAttributeValueL11nMapper.php @@ -37,10 +37,10 @@ final class BillAttributeValueL11nMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_attr_value_l11n_id' => ['name' => 'billing_attr_value_l11n_id', 'type' => 'int', 'internal' => 'id'], - 'billing_attr_value_l11n_title' => ['name' => 'billing_attr_value_l11n_title', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], - 'billing_attr_value_l11n_value' => ['name' => 'billing_attr_value_l11n_value', 'type' => 'int', 'internal' => 'ref'], - 'billing_attr_value_l11n_lang' => ['name' => 'billing_attr_value_l11n_lang', 'type' => 'string', 'internal' => 'language'], + 'billing_attr_value_l11n_id' => ['name' => 'billing_attr_value_l11n_id', 'type' => 'int', 'internal' => 'id'], + 'billing_attr_value_l11n_title' => ['name' => 'billing_attr_value_l11n_title', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], + 'billing_attr_value_l11n_value' => ['name' => 'billing_attr_value_l11n_value', 'type' => 'int', 'internal' => 'ref'], + 'billing_attr_value_l11n_lang' => ['name' => 'billing_attr_value_l11n_lang', 'type' => 'string', 'internal' => 'language'], ]; /** diff --git a/Models/Attribute/BillAttributeValueMapper.php b/Models/Attribute/BillAttributeValueMapper.php index 8b99fd9..2e54b8b 100755 --- a/Models/Attribute/BillAttributeValueMapper.php +++ b/Models/Attribute/BillAttributeValueMapper.php @@ -37,15 +37,15 @@ final class BillAttributeValueMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_attr_value_id' => ['name' => 'billing_attr_value_id', 'type' => 'int', 'internal' => 'id'], - 'billing_attr_value_default' => ['name' => 'billing_attr_value_default', 'type' => 'bool', 'internal' => 'isDefault'], - 'billing_attr_value_valueStr' => ['name' => 'billing_attr_value_valueStr', 'type' => 'string', 'internal' => 'valueStr'], - 'billing_attr_value_valueInt' => ['name' => 'billing_attr_value_valueInt', 'type' => 'int', 'internal' => 'valueInt'], - 'billing_attr_value_valueDec' => ['name' => 'billing_attr_value_valueDec', 'type' => 'float', 'internal' => 'valueDec'], - 'billing_attr_value_valueDat' => ['name' => 'billing_attr_value_valueDat', 'type' => 'DateTime', 'internal' => 'valueDat'], - 'billing_attr_value_unit' => ['name' => 'billing_attr_value_unit', 'type' => 'string', 'internal' => 'unit'], - 'billing_attr_value_deptype' => ['name' => 'billing_attr_value_deptype', 'type' => 'int', 'internal' => 'dependingAttributeType'], - 'billing_attr_value_depvalue' => ['name' => 'billing_attr_value_depvalue', 'type' => 'int', 'internal' => 'dependingAttributeValue'], + 'billing_attr_value_id' => ['name' => 'billing_attr_value_id', 'type' => 'int', 'internal' => 'id'], + 'billing_attr_value_default' => ['name' => 'billing_attr_value_default', 'type' => 'bool', 'internal' => 'isDefault'], + 'billing_attr_value_valueStr' => ['name' => 'billing_attr_value_valueStr', 'type' => 'string', 'internal' => 'valueStr'], + 'billing_attr_value_valueInt' => ['name' => 'billing_attr_value_valueInt', 'type' => 'int', 'internal' => 'valueInt'], + 'billing_attr_value_valueDec' => ['name' => 'billing_attr_value_valueDec', 'type' => 'float', 'internal' => 'valueDec'], + 'billing_attr_value_valueDat' => ['name' => 'billing_attr_value_valueDat', 'type' => 'DateTime', 'internal' => 'valueDat'], + 'billing_attr_value_unit' => ['name' => 'billing_attr_value_unit', 'type' => 'string', 'internal' => 'unit'], + 'billing_attr_value_deptype' => ['name' => 'billing_attr_value_deptype', 'type' => 'int', 'internal' => 'dependingAttributeType'], + 'billing_attr_value_depvalue' => ['name' => 'billing_attr_value_depvalue', 'type' => 'int', 'internal' => 'dependingAttributeValue'], ]; /** diff --git a/Models/Bill.php b/Models/Bill.php index e556fea..75ff7d6 100755 --- a/Models/Bill.php +++ b/Models/Bill.php @@ -17,7 +17,6 @@ namespace Modules\Billing\Models; use Modules\Admin\Models\Account; use Modules\Admin\Models\NullAccount; use Modules\ClientManagement\Models\Client; -use Modules\Media\Models\Collection; use Modules\SupplierManagement\Models\Supplier; use phpOMS\Localization\ISO4217CharEnum; use phpOMS\Localization\ISO639x1Enum; @@ -74,7 +73,9 @@ class Bill implements \JsonSerializable */ public BillType $type; - public ?Collection $template = null; + public bool $isTemplate = false; + + public bool $isArchived = false; /** * Bill status. @@ -313,20 +314,14 @@ class Bill implements \JsonSerializable public FloatInt $grossDiscount; /** - * Insurance fees in net. + * Tax amount * * @var FloatInt * @since 1.0.0 */ - public FloatInt $insurance; + public FloatInt $taxP; - /** - * Freight in net. - * - * @var FloatInt - * @since 1.0.0 - */ - public FloatInt $freight; + public ?int $accTaxCode = null; /** * Currency. @@ -379,6 +374,7 @@ class Bill implements \JsonSerializable public int $terms = 0; public ?int $paymentTerms = null; + public ?int $shippingTerms = null; /** @@ -429,6 +425,16 @@ class Bill implements \JsonSerializable */ public int $reference = 0; + public ?int $accSegment = null; + + public ?int $accSection = null; + + public ?int $accGroup = null; + + public ?int $accType = null; + + public ?string $fiAccount = null; + /** * Constructor. * @@ -444,6 +450,7 @@ class Bill implements \JsonSerializable $this->grossSales = new FloatInt(0); $this->netDiscount = new FloatInt(0); $this->grossDiscount = new FloatInt(0); + $this->taxP = new FloatInt(0); $this->billDate = new \DateTime('now'); $this->createdAt = new \DateTimeImmutable(); @@ -452,18 +459,6 @@ class Bill implements \JsonSerializable $this->type = new NullBillType(); } - /** - * Get id. - * - * @return int Model id - * - * @since 1.0.0 - */ - public function getId() : int - { - return $this->id; - } - /** * Build the invoice number. * @@ -494,7 +489,7 @@ class Bill implements \JsonSerializable $this->type->id, $this->unit, $this->accountNumber, - $this->billCountry + $this->billCountry, ], $this->type->numberFormat ); @@ -516,32 +511,6 @@ class Bill implements \JsonSerializable return $this->number; } - /** - * Get status - * - * @return int - * - * @since 1.0.0 - */ - public function getStatus() : int - { - return $this->status; - } - - /** - * Set status - * - * @param int $status Status - * - * @return void - * - * @since 1.0.0 - */ - public function setStatus(int $status) : void - { - $this->status = $status; - } - /** * Get paymentStatus * @@ -568,96 +537,6 @@ class Bill implements \JsonSerializable $this->paymentStatus = $paymentStatus; } - /** - * Set currency. - * - * @param string $currency Currency - * - * @return void - * - * @since 1.0.0 - */ - public function setCurrency(string $currency) : void - { - $this->currency = $currency; - } - - /** - * Get currency. - * - * @return string - * - * @since 1.0.0 - */ - public function getCurrency() : string - { - return $this->currency; - } - - /** - * Get vouchers. - * - * @return array - * - * @since 1.0.0 - */ - public function getVouchers() : array - { - return $this->vouchers; - } - - /** - * Add voucher. - * - * @param string $voucher Voucher code - * - * @return void - * - * @since 1.0.0 - */ - public function addVoucher(string $voucher) : void - { - $this->vouchers[] = $voucher; - } - - /** - * Get tracking ids for shipment. - * - * @return array - * - * @since 1.0.0 - */ - public function getTrackings() : array - { - return $this->trackings; - } - - /** - * Add tracking id. - * - * @param string $tracking Tracking id - * - * @return void - * - * @since 1.0.0 - */ - public function addTracking(string $tracking) : void - { - $this->trackings[] = $tracking; - } - - /** - * Get Bill elements. - * - * @return BillElement[] - * - * @since 1.0.0 - */ - public function getElements() : array - { - return $this->elements; - } - /** * Add Bill element. * @@ -671,16 +550,16 @@ class Bill implements \JsonSerializable { $this->elements[] = $element; - $this->netProfit->add($element->totalProfitNet->getInt()); - $this->grossProfit->add($element->totalProfitGross->getInt()); - $this->netCosts->add($element->totalPurchasePriceNet->getInt()); - $this->grossCosts->add($element->totalPurchasePriceGross->getInt()); - $this->netSales->add($element->totalSalesPriceNet->getInt()); - $this->grossSales->add($element->totalSalesPriceGross->getInt()); - $this->netDiscount->add($element->totalDiscountP->getInt()); + $this->netProfit->value += $element->totalProfitNet->value; + $this->grossProfit->value += $element->totalProfitGross->value; + $this->netCosts->value += $element->totalPurchasePriceNet->value; + $this->grossCosts->value += $element->totalPurchasePriceGross->value; + $this->netSales->value += $element->totalSalesPriceNet->value; + $this->grossSales->value += $element->totalSalesPriceGross->value; + $this->netDiscount->value += $element->totalDiscountP->value; // @todo Discount might be in quantities - $this->grossDiscount->add((int) ($element->taxR->getInt() * $element->totalDiscountP->getInt() / 10000)); + $this->grossDiscount->value += (int) ($element->taxR->value * $element->totalDiscountP->value / 10000); } /** @@ -689,21 +568,21 @@ class Bill implements \JsonSerializable public function toArray() : array { return [ - 'id' => $this->id, - 'number' => $this->number, - 'type' => $this->type, - 'shipTo' => $this->shipTo, - 'shipFAO' => $this->shipFAO, - 'shipAddress' => $this->shipAddress, - 'shipCity' => $this->shipCity, - 'shipZip' => $this->shipZip, - 'shipCountry' => $this->shipCountry, - 'billTo' => $this->billTo, - 'billFAO' => $this->billFAO, - 'billAddress' => $this->billAddress, - 'billCity' => $this->billCity, - 'billZip' => $this->billZip, - 'billCountry' => $this->billCountry, + 'id' => $this->id, + 'number' => $this->number, + 'type' => $this->type, + 'shipTo' => $this->shipTo, + 'shipFAO' => $this->shipFAO, + 'shipAddress' => $this->shipAddress, + 'shipCity' => $this->shipCity, + 'shipZip' => $this->shipZip, + 'shipCountry' => $this->shipCountry, + 'billTo' => $this->billTo, + 'billFAO' => $this->billFAO, + 'billAddress' => $this->billAddress, + 'billCity' => $this->billCity, + 'billZip' => $this->billZip, + 'billCountry' => $this->billCountry, ]; } diff --git a/Models/BillElement.php b/Models/BillElement.php index 98f917a..26ae02a 100755 --- a/Models/BillElement.php +++ b/Models/BillElement.php @@ -14,7 +14,9 @@ declare(strict_types=1); namespace Modules\Billing\Models; +use Modules\Billing\Models\Tax\TaxCombination; use Modules\Finance\Models\TaxCode; +use Modules\ItemManagement\Models\Container; use Modules\ItemManagement\Models\Item; use Modules\ItemManagement\Models\NullItem; use phpOMS\Stdlib\Base\FloatInt; @@ -42,18 +44,38 @@ class BillElement implements \JsonSerializable public ?Item $item = null; + public ?Container $container = null; + public string $itemNumber = ''; public string $itemName = ''; public string $itemDescription = ''; - public int $quantity = 0; + /** + * Line quantity + * + * Careful this also includes the bonus items defined in $discountQ! + * + * @var FloatInt + * @since 1.0.0 + */ + public FloatInt $quantity; public ?Subscription $subscription = null; + /** + * Single unit price + * + * Careful this is NOT corrected by bonus items defined in $discountQ + * + * @var FloatInt + * @since 1.0.0 + */ public FloatInt $singleSalesPriceNet; + public FloatInt $effectiveSingleSalesPriceNet; + public FloatInt $singleSalesPriceGross; public FloatInt $totalSalesPriceNet; @@ -64,9 +86,9 @@ class BillElement implements \JsonSerializable public FloatInt $totalDiscountP; - public ?FloatInt $singleDiscountR = null; + public FloatInt $singleDiscountR; - public ?FloatInt $discountQ = null; + public FloatInt $discountQ; public FloatInt $singleListPriceNet; @@ -92,6 +114,22 @@ class BillElement implements \JsonSerializable public FloatInt $totalProfitGross; + public ?int $itemSegment = null; + + public ?int $itemSection = null; + + public ?int $itemSalesGroup = null; + + public ?int $itemProductGroup = null; + + public ?int $itemType = null; + + public string $fiAccount = ''; + + public ?string $costcenter = null; + + public ?string $costobject = null; + /** * Tax amount * @@ -128,6 +166,9 @@ class BillElement implements \JsonSerializable public Bill $bill; + // Distribution of lots/sn and from which stock location + public array $identifiers = []; + /** * Constructor. * @@ -137,6 +178,8 @@ class BillElement implements \JsonSerializable { $this->bill = new NullBill(); + $this->quantity = new FloatInt(); + $this->singleListPriceNet = new FloatInt(); $this->singleListPriceGross = new FloatInt(); @@ -146,6 +189,8 @@ class BillElement implements \JsonSerializable $this->singleSalesPriceNet = new FloatInt(); $this->singleSalesPriceGross = new FloatInt(); + $this->effectiveSingleSalesPriceNet = new FloatInt(); + $this->totalSalesPriceNet = new FloatInt(); $this->totalSalesPriceGross = new FloatInt(); @@ -163,23 +208,13 @@ class BillElement implements \JsonSerializable $this->singleDiscountP = new FloatInt(); $this->totalDiscountP = new FloatInt(); + $this->singleDiscountR = new FloatInt(); + $this->discountQ = new FloatInt(); $this->taxP = new FloatInt(); $this->taxR = new FloatInt(); } - /** - * Get id. - * - * @return int Model id - * - * @since 1.0.0 - */ - public function getId() : int - { - return $this->id; - } - /** * Set the element quantity. * @@ -196,19 +231,30 @@ class BillElement implements \JsonSerializable } $this->quantity = $quantity; - // @todo recalculate all the prices!!! + + $this->recalculatePrices(); } - /** - * Get quantity. - * - * @return int - * - * @since 1.0.0 - */ - public function getQuantity() : int + public function recalculatePrices() : void { - return $this->quantity; + $this->totalListPriceNet->value = (int) \round(($this->quantity->getNormalizedValue() - $this->discountQ->getNormalizedValue()) * $this->singleListPriceNet->value, 0); + $this->totalSalesPriceNet->value = (int) \round(($this->quantity->getNormalizedValue() - $this->discountQ->getNormalizedValue()) * $this->singleListPriceNet->value, 0); + + $this->singleProfitNet->value = $this->singleSalesPriceNet->value - $this->singlePurchasePriceNet->value; + $this->totalProfitNet->value = $this->totalSalesPriceNet->value - $this->totalPurchasePriceNet->value; + + $this->taxP->value = (int) \round($this->taxR->value / 1000000 * $this->totalSalesPriceNet->value, 0); + + $this->singleListPriceGross->value = (int) \round($this->singleListPriceNet->value + $this->singleListPriceNet->value * $this->taxR->value / 10000, 0); + $this->totalListPriceGross->value = (int) \round($this->totalListPriceNet->value + $this->totalListPriceNet->value * $this->taxR->value / 10000, 0); + $this->singleSalesPriceGross->value = (int) \round($this->singleSalesPriceNet->value + $this->singleSalesPriceNet->value * $this->taxR->value / 10000, 0); + $this->totalSalesPriceGross->value = (int) \round($this->totalSalesPriceNet->value + $this->totalSalesPriceNet->value * $this->taxR->value / 10000, 0); + + $this->singleProfitGross->value = $this->singleSalesPriceGross->value - $this->singlePurchasePriceGross->value; + $this->totalProfitGross->value = (int) \round(($this->quantity->getNormalizedValue() - $this->discountQ->getNormalizedValue()) * ($this->totalSalesPriceGross->value - $this->totalPurchasePriceGross->value), 0); + + // important because the quantity includes $discountQ + $this->effectiveSingleSalesPriceNet->value = (int) \round($this->totalSalesPriceNet->value / ($this->quantity->value / 10000)); } /** @@ -229,7 +275,7 @@ class BillElement implements \JsonSerializable * Create element from item * * @param Item $item Item - * @param TaxCode $code Tax code used for gross amount calculation + * @param TaxCode $taxCode Tax code used for gross amount calculation * @param int $quantity Quantity * @param int $bill Bill * @@ -237,41 +283,33 @@ class BillElement implements \JsonSerializable * * @since 1.0.0 */ - public static function fromItem(Item $item, TaxCode $code, int $quantity = 1, int $bill = 0) : self + public static function fromItem( + Item $item, + TaxCombination $taxCombination, + int $quantity = 10000, + int $bill = 0, + ?Container $container = null + ) : self { $element = new self(); $element->bill = new NullBill($bill); $element->item = empty($item->id) ? null : $item; + $element->container = empty($container->id) ? null : $container; $element->itemNumber = $item->number; $element->itemName = $item->getL11n('name1')->content; $element->itemDescription = $item->getL11n('description_short')->content; - $element->quantity = $quantity; + $element->quantity->value = $quantity; - // @todo Use pricing instead of the default sales price - // @todo discounts might be in quantities - $element->singleListPriceNet->setInt($item->salesPrice->getInt()); - $element->totalListPriceNet->setInt($element->quantity * $item->salesPrice->getInt()); - $element->singleSalesPriceNet->setInt($item->salesPrice->getInt()); - $element->totalSalesPriceNet->setInt($element->quantity * $item->salesPrice->getInt()); - $element->singlePurchasePriceNet->setInt($item->purchasePrice->getInt()); - $element->totalPurchasePriceNet->setInt($element->quantity * $item->purchasePrice->getInt()); + $element->taxR = new FloatInt($taxCombination->taxCode->percentageInvoice); + $element->taxCode = $taxCombination->taxCode->abbr; + $element->fiAccount = $taxCombination->account; - $element->singleProfitNet->setInt($element->singleSalesPriceNet->getInt() - $element->singlePurchasePriceNet->getInt()); - $element->totalProfitNet->setInt($element->totalSalesPriceNet->getInt() - $element->totalPurchasePriceNet->getInt()); + // @todo the purchase price is based on lot/sn/avg prices if available + $element->singlePurchasePriceNet->value = $item->purchasePrice->value; + $element->totalPurchasePriceNet->value = (int) ($element->quantity->getNormalizedValue() * $item->purchasePrice->value); - $element->taxP = new FloatInt((int) (($code->percentageInvoice * $element->totalSalesPriceNet->getInt()) / 10000)); - $element->taxR = new FloatInt($code->percentageInvoice); - $element->taxCode = $code->abbr; - - $element->singleListPriceGross->setInt((int) ($element->singleListPriceNet->getInt() + $element->singleListPriceNet->getInt() * $element->taxR->getInt() / 10000)); - $element->totalListPriceGross->setInt((int) ($element->totalListPriceNet->getInt() + $element->totalListPriceNet->getInt() * $element->taxR->getInt() / 10000)); - $element->singleSalesPriceGross->setInt((int) ($element->singleSalesPriceNet->getInt() + $element->singleSalesPriceNet->getInt() * $element->taxR->getInt() / 10000)); - $element->totalSalesPriceGross->setInt((int) ($element->totalSalesPriceNet->getInt() + $element->totalSalesPriceNet->getInt() * $element->taxR->getInt() / 10000)); - $element->singlePurchasePriceGross->setInt((int) ($element->singlePurchasePriceNet->getInt() + $element->singlePurchasePriceNet->getInt() * $element->taxR->getInt() / 10000)); - $element->totalPurchasePriceGross->setInt((int) ($element->totalPurchasePriceNet->getInt() + $element->totalPurchasePriceNet->getInt() * $element->taxR->getInt() / 10000)); - - $element->singleProfitGross->setInt($element->singleSalesPriceGross->getInt() - $element->singlePurchasePriceGross->getInt()); - $element->totalProfitGross->setInt($element->quantity * ($element->totalSalesPriceGross->getInt() - $element->totalPurchasePriceGross->getInt())); + $element->singlePurchasePriceGross->value = (int) \round($element->singlePurchasePriceNet->value + $element->singlePurchasePriceNet->value * $element->taxR->value / 10000, 0); + $element->totalPurchasePriceGross->value = (int) \round($element->totalPurchasePriceNet->value + $element->totalPurchasePriceNet->value * $element->taxR->value / 10000, 0); if ($element->bill->id !== 0 && $item->getAttribute('subscription')->value->getValue() === 1 @@ -281,8 +319,7 @@ class BillElement implements \JsonSerializable $element->subscription->bill = $element->bill->id; $element->subscription->item = $element->item->id; $element->subscription->start = new \DateTime('now'); // @todo change to bill performanceDate - $element->subscription->end = new SmartDateTime('now'); // @todo depends on subscription type - $element->subscription->end->smartModify(m: 1); + $element->subscription->end = (new SmartDateTime('now'))->smartModify(m: 1); // @todo depends on subscription type $element->subscription->quantity = $element->quantity; $element->subscription->autoRenew = $item->getAttribute('subscription_renewal_type')->value->getValue() === 1; diff --git a/Models/BillElementMapper.php b/Models/BillElementMapper.php index 19ade53..2abe925 100755 --- a/Models/BillElementMapper.php +++ b/Models/BillElementMapper.php @@ -14,6 +14,7 @@ declare(strict_types=1); namespace Modules\Billing\Models; +use Modules\ItemManagement\Models\ContainerMapper; use Modules\ItemManagement\Models\ItemMapper; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; @@ -37,28 +38,30 @@ final class BillElementMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_bill_element_id' => ['name' => 'billing_bill_element_id', 'type' => 'int', 'internal' => 'id'], - 'billing_bill_element_order' => ['name' => 'billing_bill_element_order', 'type' => 'int', 'internal' => 'order'], - 'billing_bill_element_item' => ['name' => 'billing_bill_element_item', 'type' => 'int', 'internal' => 'item'], - 'billing_bill_element_item_number' => ['name' => 'billing_bill_element_item_number', 'type' => 'string', 'internal' => 'itemNumber'], - 'billing_bill_element_item_name' => ['name' => 'billing_bill_element_item_name', 'type' => 'string', 'internal' => 'itemName'], - 'billing_bill_element_item_desc' => ['name' => 'billing_bill_element_item_desc', 'type' => 'string', 'internal' => 'itemDescription'], - 'billing_bill_element_quantity' => ['name' => 'billing_bill_element_quantity', 'type' => 'int', 'internal' => 'quantity', 'private' => true], + 'billing_bill_element_id' => ['name' => 'billing_bill_element_id', 'type' => 'int', 'internal' => 'id'], + 'billing_bill_element_order' => ['name' => 'billing_bill_element_order', 'type' => 'int', 'internal' => 'order'], + 'billing_bill_element_item' => ['name' => 'billing_bill_element_item', 'type' => 'int', 'internal' => 'item'], + 'billing_bill_element_container' => ['name' => 'billing_bill_element_container', 'type' => 'int', 'internal' => 'container'], + 'billing_bill_element_item_number' => ['name' => 'billing_bill_element_item_number', 'type' => 'string', 'internal' => 'itemNumber'], + 'billing_bill_element_item_name' => ['name' => 'billing_bill_element_item_name', 'type' => 'string', 'internal' => 'itemName'], + 'billing_bill_element_item_desc' => ['name' => 'billing_bill_element_item_desc', 'type' => 'string', 'internal' => 'itemDescription'], + 'billing_bill_element_quantity' => ['name' => 'billing_bill_element_quantity', 'type' => 'Serializable', 'internal' => 'quantity', 'private' => true], - 'billing_bill_element_single_netlistprice' => ['name' => 'billing_bill_element_single_netlistprice', 'type' => 'Serializable', 'internal' => 'singleListPriceNet'], - 'billing_bill_element_single_grosslistprice' => ['name' => 'billing_bill_element_single_grosslistprice', 'type' => 'Serializable', 'internal' => 'singleListPriceGross'], - 'billing_bill_element_total_netlistprice' => ['name' => 'billing_bill_element_total_netlistprice', 'type' => 'Serializable', 'internal' => 'totalListPriceNet'], - 'billing_bill_element_total_grosslistprice' => ['name' => 'billing_bill_element_total_grosslistprice', 'type' => 'Serializable', 'internal' => 'totalListPriceGross'], + 'billing_bill_element_single_netlistprice' => ['name' => 'billing_bill_element_single_netlistprice', 'type' => 'Serializable', 'internal' => 'singleListPriceNet'], + 'billing_bill_element_single_grosslistprice' => ['name' => 'billing_bill_element_single_grosslistprice', 'type' => 'Serializable', 'internal' => 'singleListPriceGross'], + 'billing_bill_element_total_netlistprice' => ['name' => 'billing_bill_element_total_netlistprice', 'type' => 'Serializable', 'internal' => 'totalListPriceNet'], + 'billing_bill_element_total_grosslistprice' => ['name' => 'billing_bill_element_total_grosslistprice', 'type' => 'Serializable', 'internal' => 'totalListPriceGross'], - 'billing_bill_element_single_netsalesprice' => ['name' => 'billing_bill_element_single_netsalesprice', 'type' => 'Serializable', 'internal' => 'singleSalesPriceNet'], - 'billing_bill_element_single_grosssalesprice' => ['name' => 'billing_bill_element_single_grosssalesprice', 'type' => 'Serializable', 'internal' => 'singleSalesPriceGross'], - 'billing_bill_element_total_netsalesprice' => ['name' => 'billing_bill_element_total_netsalesprice', 'type' => 'Serializable', 'internal' => 'totalSalesPriceNet'], - 'billing_bill_element_total_grosssalesprice' => ['name' => 'billing_bill_element_total_grosssalesprice', 'type' => 'Serializable', 'internal' => 'totalSalesPriceGross'], + 'billing_bill_element_single_netsalesprice' => ['name' => 'billing_bill_element_single_netsalesprice', 'type' => 'Serializable', 'internal' => 'singleSalesPriceNet'], + 'billing_bill_element_single_effectivenetsalesprice' => ['name' => 'billing_bill_element_single_effectivenetsalesprice', 'type' => 'Serializable', 'internal' => 'effectiveSingleSalesPriceNet'], + 'billing_bill_element_single_grosssalesprice' => ['name' => 'billing_bill_element_single_grosssalesprice', 'type' => 'Serializable', 'internal' => 'singleSalesPriceGross'], + 'billing_bill_element_total_netsalesprice' => ['name' => 'billing_bill_element_total_netsalesprice', 'type' => 'Serializable', 'internal' => 'totalSalesPriceNet'], + 'billing_bill_element_total_grosssalesprice' => ['name' => 'billing_bill_element_total_grosssalesprice', 'type' => 'Serializable', 'internal' => 'totalSalesPriceGross'], - 'billing_bill_element_single_netprofit' => ['name' => 'billing_bill_element_single_netprofit', 'type' => 'Serializable', 'internal' => 'singleProfitNet'], - 'billing_bill_element_single_grossprofit' => ['name' => 'billing_bill_element_single_grossprofit', 'type' => 'Serializable', 'internal' => 'singleProfitGross'], - 'billing_bill_element_total_netprofit' => ['name' => 'billing_bill_element_total_netprofit', 'type' => 'Serializable', 'internal' => 'totalProfitNet'], - 'billing_bill_element_total_grossprofit' => ['name' => 'billing_bill_element_total_grossprofit', 'type' => 'Serializable', 'internal' => 'totalProfitGross'], + 'billing_bill_element_single_netprofit' => ['name' => 'billing_bill_element_single_netprofit', 'type' => 'Serializable', 'internal' => 'singleProfitNet'], + 'billing_bill_element_single_grossprofit' => ['name' => 'billing_bill_element_single_grossprofit', 'type' => 'Serializable', 'internal' => 'singleProfitGross'], + 'billing_bill_element_total_netprofit' => ['name' => 'billing_bill_element_total_netprofit', 'type' => 'Serializable', 'internal' => 'totalProfitNet'], + 'billing_bill_element_total_grossprofit' => ['name' => 'billing_bill_element_total_grossprofit', 'type' => 'Serializable', 'internal' => 'totalProfitGross'], 'billing_bill_element_single_netpurchaseprice' => ['name' => 'billing_bill_element_single_netpurchaseprice', 'type' => 'Serializable', 'internal' => 'singlePurchasePriceNet'], 'billing_bill_element_single_grosspurchaseprice' => ['name' => 'billing_bill_element_single_grosspurchaseprice', 'type' => 'Serializable', 'internal' => 'singlePurchasePriceGross'], @@ -66,9 +69,19 @@ final class BillElementMapper extends DataMapperFactory 'billing_bill_element_total_grosspurchaseprice' => ['name' => 'billing_bill_element_total_grosspurchaseprice', 'type' => 'Serializable', 'internal' => 'totalPurchasePriceGross'], 'billing_bill_element_bill' => ['name' => 'billing_bill_element_bill', 'type' => 'int', 'internal' => 'bill'], - 'billing_bill_element_tax_type' => ['name' => 'billing_bill_element_tax_type', 'type' => 'string', 'internal' => 'taxCode'], - 'billing_bill_element_tax_price' => ['name' => 'billing_bill_element_tax_price', 'type' => 'Serializable', 'internal' => 'taxP'], - 'billing_bill_element_tax_percentage' => ['name' => 'billing_bill_element_tax_percentage', 'type' => 'Serializable', 'internal' => 'taxR'], + 'billing_bill_element_tax_type' => ['name' => 'billing_bill_element_tax_type', 'type' => 'string', 'internal' => 'taxCode'], + 'billing_bill_element_tax_price' => ['name' => 'billing_bill_element_tax_price', 'type' => 'Serializable', 'internal' => 'taxP'], + 'billing_bill_element_tax_percentage' => ['name' => 'billing_bill_element_tax_percentage', 'type' => 'Serializable', 'internal' => 'taxR'], + + 'billing_bill_element_segment' => ['name' => 'billing_bill_element_segment', 'type' => 'int', 'internal' => 'itemSegment'], + 'billing_bill_element_section' => ['name' => 'billing_bill_element_section', 'type' => 'int', 'internal' => 'itemSection'], + 'billing_bill_element_salesgroup' => ['name' => 'billing_bill_element_salesgroup', 'type' => 'int', 'internal' => 'itemSalesGroup'], + 'billing_bill_element_productgroup' => ['name' => 'billing_bill_element_productgroup', 'type' => 'int', 'internal' => 'itemProductGroup'], + 'billing_bill_element_itemtype' => ['name' => 'billing_bill_element_itemtype', 'type' => 'int', 'internal' => 'itemType'], + + 'billing_bill_element_fiaccount' => ['name' => 'billing_bill_element_fiaccount', 'type' => 'string', 'internal' => 'fiAccount'], + 'billing_bill_element_costcenter' => ['name' => 'billing_bill_element_costcenter', 'type' => 'string', 'internal' => 'costcenter'], + 'billing_bill_element_costobject' => ['name' => 'billing_bill_element_costobject', 'type' => 'string', 'internal' => 'costobject'], ]; /** @@ -79,8 +92,8 @@ final class BillElementMapper extends DataMapperFactory */ public const BELONGS_TO = [ 'bill' => [ - 'mapper' => BillMapper::class, - 'external' => 'billing_bill_element_bill', + 'mapper' => BillMapper::class, + 'external' => 'billing_bill_element_bill', ], ]; @@ -92,12 +105,16 @@ final class BillElementMapper extends DataMapperFactory */ public const OWNS_ONE = [ 'subscription' => [ - 'mapper' => SubscriptionMapper::class, - 'external' => 'billing_bill_element_subscription', + 'mapper' => SubscriptionMapper::class, + 'external' => 'billing_bill_element_subscription', ], 'item' => [ - 'mapper' => ItemMapper::class, - 'external' => 'billing_bill_element_item', + 'mapper' => ItemMapper::class, + 'external' => 'billing_bill_element_item', + ], + 'container' => [ + 'mapper' => ContainerMapper::class, + 'external' => 'billing_bill_element_container', ], ]; diff --git a/Models/BillMapper.php b/Models/BillMapper.php index 2ce5b9b..84fb55f 100755 --- a/Models/BillMapper.php +++ b/Models/BillMapper.php @@ -45,55 +45,63 @@ class BillMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_bill_id' => ['name' => 'billing_bill_id', 'type' => 'int', 'internal' => 'id'], - 'billing_bill_sequence' => ['name' => 'billing_bill_sequence', 'type' => 'int', 'internal' => 'sequence'], - 'billing_bill_number' => ['name' => 'billing_bill_number', 'type' => 'string', 'internal' => 'number'], - 'billing_bill_type' => ['name' => 'billing_bill_type', 'type' => 'int', 'internal' => 'type'], - 'billing_bill_template' => ['name' => 'billing_bill_template', 'type' => 'int', 'internal' => 'template'], - 'billing_bill_header' => ['name' => 'billing_bill_header', 'type' => 'string', 'internal' => 'header'], - 'billing_bill_footer' => ['name' => 'billing_bill_footer', 'type' => 'string', 'internal' => 'footer'], - 'billing_bill_info' => ['name' => 'billing_bill_info', 'type' => 'string', 'internal' => 'info'], - 'billing_bill_status' => ['name' => 'billing_bill_status', 'type' => 'int', 'internal' => 'status'], - 'billing_bill_paymentstatus' => ['name' => 'billing_bill_paymentstatus', 'type' => 'int', 'internal' => 'paymentStatus'], - 'billing_bill_shipTo' => ['name' => 'billing_bill_shipTo', 'type' => 'string', 'internal' => 'shipTo'], - 'billing_bill_shipFAO' => ['name' => 'billing_bill_shipFAO', 'type' => 'string', 'internal' => 'shipFAO'], - 'billing_bill_shipAddr' => ['name' => 'billing_bill_shipAddr', 'type' => 'string', 'internal' => 'shipAddress'], - 'billing_bill_shipCity' => ['name' => 'billing_bill_shipCity', 'type' => 'string', 'internal' => 'shipCity'], - 'billing_bill_shipZip' => ['name' => 'billing_bill_shipZip', 'type' => 'string', 'internal' => 'shipZip'], - 'billing_bill_shipCountry' => ['name' => 'billing_bill_shipCountry', 'type' => 'string', 'internal' => 'shipCountry'], - 'billing_bill_billTo' => ['name' => 'billing_bill_billTo', 'type' => 'string', 'internal' => 'billTo'], - 'billing_bill_billFAO' => ['name' => 'billing_bill_billFAO', 'type' => 'string', 'internal' => 'billFAO'], - 'billing_bill_billAddr' => ['name' => 'billing_bill_billAddr', 'type' => 'string', 'internal' => 'billAddress'], - 'billing_bill_billCity' => ['name' => 'billing_bill_billCity', 'type' => 'string', 'internal' => 'billCity'], - 'billing_bill_billZip' => ['name' => 'billing_bill_billZip', 'type' => 'string', 'internal' => 'billZip'], - 'billing_bill_billCountry' => ['name' => 'billing_bill_billCountry', 'type' => 'string', 'internal' => 'billCountry'], - 'billing_bill_netprofit' => ['name' => 'billing_bill_netprofit', 'type' => 'Serializable', 'internal' => 'netProfit'], - 'billing_bill_grossprofit' => ['name' => 'billing_bill_grossprofit', 'type' => 'Serializable', 'internal' => 'grossProfit'], - 'billing_bill_netcosts' => ['name' => 'billing_bill_netcosts', 'type' => 'Serializable', 'internal' => 'netCosts'], - 'billing_bill_grosscosts' => ['name' => 'billing_bill_grosscosts', 'type' => 'Serializable', 'internal' => 'grossCosts'], - 'billing_bill_netsales' => ['name' => 'billing_bill_netsales', 'type' => 'Serializable', 'internal' => 'netSales'], - 'billing_bill_grosssales' => ['name' => 'billing_bill_grosssales', 'type' => 'Serializable', 'internal' => 'grossSales'], - 'billing_bill_netdiscount' => ['name' => 'billing_bill_netdiscount', 'type' => 'Serializable', 'internal' => 'netDiscount'], - 'billing_bill_grossdiscount' => ['name' => 'billing_bill_grossdiscount', 'type' => 'Serializable', 'internal' => 'grossDiscount'], - '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'], - 'billing_bill_referral_name' => ['name' => 'billing_bill_referral_name', 'type' => 'string', 'internal' => 'referralName'], - 'billing_bill_reference' => ['name' => 'billing_bill_reference', 'type' => 'int', 'internal' => 'reference'], - 'billing_bill_payment' => ['name' => 'billing_bill_payment', 'type' => 'int', 'internal' => 'payment'], - 'billing_bill_payment_text' => ['name' => 'billing_bill_payment_text', 'type' => 'string', 'internal' => 'paymentText'], - 'billing_bill_paymentterms' => ['name' => 'billing_bill_paymentterms', 'type' => 'int', 'internal' => 'paymentTerms'], - 'billing_bill_paymentterms_text'=> ['name' => 'billing_bill_paymentterms_text', 'type' => 'string', 'internal' => 'termsText'], - 'billing_bill_ship_type' => ['name' => 'billing_bill_ship_type', 'type' => 'int', 'internal' => 'shippingTerms'], - 'billing_bill_ship_text' => ['name' => 'billing_bill_ship_text', 'type' => 'string', 'internal' => 'shippingText'], - 'billing_bill_account_no' => ['name' => 'billing_bill_account_no', 'type' => 'string', 'internal' => 'accountNumber'], - 'billing_bill_client' => ['name' => 'billing_bill_client', 'type' => 'int', 'internal' => 'client'], - 'billing_bill_supplier' => ['name' => 'billing_bill_supplier', 'type' => 'int', 'internal' => 'supplier'], - 'billing_bill_created_by' => ['name' => 'billing_bill_created_by', 'type' => 'int', 'internal' => 'createdBy', 'readonly' => true], - 'billing_bill_date' => ['name' => 'billing_bill_date', 'type' => 'DateTime', 'internal' => 'billDate'], - 'billing_bill_performance_date' => ['name' => 'billing_bill_performance_date', 'type' => 'DateTime', 'internal' => 'performanceDate', 'readonly' => true], - 'billing_bill_created_at' => ['name' => 'billing_bill_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt', 'readonly' => true], - 'billing_bill_unit' => ['name' => 'billing_bill_unit', 'type' => 'int', 'internal' => 'unit'], + 'billing_bill_id' => ['name' => 'billing_bill_id', 'type' => 'int', 'internal' => 'id'], + 'billing_bill_sequence' => ['name' => 'billing_bill_sequence', 'type' => 'int', 'internal' => 'sequence'], + 'billing_bill_number' => ['name' => 'billing_bill_number', 'type' => 'string', 'internal' => 'number'], + 'billing_bill_type' => ['name' => 'billing_bill_type', 'type' => 'int', 'internal' => 'type'], + 'billing_bill_template' => ['name' => 'billing_bill_template', 'type' => 'bool', 'internal' => 'isTemplate'], + 'billing_bill_archived' => ['name' => 'billing_bill_archived', 'type' => 'bool', 'internal' => 'isArchived'], + 'billing_bill_header' => ['name' => 'billing_bill_header', 'type' => 'string', 'internal' => 'header'], + 'billing_bill_footer' => ['name' => 'billing_bill_footer', 'type' => 'string', 'internal' => 'footer'], + 'billing_bill_info' => ['name' => 'billing_bill_info', 'type' => 'string', 'internal' => 'info'], + 'billing_bill_status' => ['name' => 'billing_bill_status', 'type' => 'int', 'internal' => 'status'], + 'billing_bill_paymentstatus' => ['name' => 'billing_bill_paymentstatus', 'type' => 'int', 'internal' => 'paymentStatus'], + 'billing_bill_shipTo' => ['name' => 'billing_bill_shipTo', 'type' => 'string', 'internal' => 'shipTo'], + 'billing_bill_shipFAO' => ['name' => 'billing_bill_shipFAO', 'type' => 'string', 'internal' => 'shipFAO'], + 'billing_bill_shipAddr' => ['name' => 'billing_bill_shipAddr', 'type' => 'string', 'internal' => 'shipAddress'], + 'billing_bill_shipCity' => ['name' => 'billing_bill_shipCity', 'type' => 'string', 'internal' => 'shipCity'], + 'billing_bill_shipZip' => ['name' => 'billing_bill_shipZip', 'type' => 'string', 'internal' => 'shipZip'], + 'billing_bill_shipCountry' => ['name' => 'billing_bill_shipCountry', 'type' => 'string', 'internal' => 'shipCountry'], + 'billing_bill_billTo' => ['name' => 'billing_bill_billTo', 'type' => 'string', 'internal' => 'billTo'], + 'billing_bill_billFAO' => ['name' => 'billing_bill_billFAO', 'type' => 'string', 'internal' => 'billFAO'], + 'billing_bill_billAddr' => ['name' => 'billing_bill_billAddr', 'type' => 'string', 'internal' => 'billAddress'], + 'billing_bill_billCity' => ['name' => 'billing_bill_billCity', 'type' => 'string', 'internal' => 'billCity'], + 'billing_bill_billZip' => ['name' => 'billing_bill_billZip', 'type' => 'string', 'internal' => 'billZip'], + 'billing_bill_billCountry' => ['name' => 'billing_bill_billCountry', 'type' => 'string', 'internal' => 'billCountry'], + 'billing_bill_netprofit' => ['name' => 'billing_bill_netprofit', 'type' => 'Serializable', 'internal' => 'netProfit'], + 'billing_bill_grossprofit' => ['name' => 'billing_bill_grossprofit', 'type' => 'Serializable', 'internal' => 'grossProfit'], + 'billing_bill_netcosts' => ['name' => 'billing_bill_netcosts', 'type' => 'Serializable', 'internal' => 'netCosts'], + 'billing_bill_grosscosts' => ['name' => 'billing_bill_grosscosts', 'type' => 'Serializable', 'internal' => 'grossCosts'], + 'billing_bill_netsales' => ['name' => 'billing_bill_netsales', 'type' => 'Serializable', 'internal' => 'netSales'], + 'billing_bill_grosssales' => ['name' => 'billing_bill_grosssales', 'type' => 'Serializable', 'internal' => 'grossSales'], + 'billing_bill_netdiscount' => ['name' => 'billing_bill_netdiscount', 'type' => 'Serializable', 'internal' => 'netDiscount'], + 'billing_bill_grossdiscount' => ['name' => 'billing_bill_grossdiscount', 'type' => 'Serializable', 'internal' => 'grossDiscount'], + 'billing_bill_taxp' => ['name' => 'billing_bill_taxp', 'type' => 'Serializable', 'internal' => 'taxP'], + 'billing_bill_fiaccount' => ['name' => 'billing_bill_fiaccount', 'type' => 'string', 'internal' => 'fiAccount'], + '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'], + 'billing_bill_referral_name' => ['name' => 'billing_bill_referral_name', 'type' => 'string', 'internal' => 'referralName'], + 'billing_bill_reference' => ['name' => 'billing_bill_reference', 'type' => 'int', 'internal' => 'reference'], + 'billing_bill_accsegment' => ['name' => 'billing_bill_accsegment', 'type' => 'int', 'internal' => 'accSegment'], + 'billing_bill_accsection' => ['name' => 'billing_bill_accsection', 'type' => 'int', 'internal' => 'accSection'], + 'billing_bill_accgroup' => ['name' => 'billing_bill_accgroup', 'type' => 'int', 'internal' => 'accGroup'], + 'billing_bill_acctype' => ['name' => 'billing_bill_acctype', 'type' => 'int', 'internal' => 'accType'], + 'billing_bill_payment' => ['name' => 'billing_bill_payment', 'type' => 'int', 'internal' => 'payment'], + 'billing_bill_payment_text' => ['name' => 'billing_bill_payment_text', 'type' => 'string', 'internal' => 'paymentText'], + 'billing_bill_paymentterms' => ['name' => 'billing_bill_paymentterms', 'type' => 'int', 'internal' => 'paymentTerms'], + 'billing_bill_paymentterms_text' => ['name' => 'billing_bill_paymentterms_text', 'type' => 'string', 'internal' => 'termsText'], + 'billing_bill_ship_type' => ['name' => 'billing_bill_ship_type', 'type' => 'int', 'internal' => 'shippingTerms'], + 'billing_bill_ship_text' => ['name' => 'billing_bill_ship_text', 'type' => 'string', 'internal' => 'shippingText'], + 'billing_bill_account_no' => ['name' => 'billing_bill_account_no', 'type' => 'string', 'internal' => 'accountNumber'], + 'billing_bill_tax_type' => ['name' => 'billing_bill_tax_type', 'type' => 'int', 'internal' => 'accTaxCode'], + 'billing_bill_client' => ['name' => 'billing_bill_client', 'type' => 'int', 'internal' => 'client'], + 'billing_bill_supplier' => ['name' => 'billing_bill_supplier', 'type' => 'int', 'internal' => 'supplier'], + 'billing_bill_created_by' => ['name' => 'billing_bill_created_by', 'type' => 'int', 'internal' => 'createdBy', 'readonly' => true], + 'billing_bill_date' => ['name' => 'billing_bill_date', 'type' => 'DateTime', 'internal' => 'billDate'], + 'billing_bill_performance_date' => ['name' => 'billing_bill_performance_date', 'type' => 'DateTime', 'internal' => 'performanceDate', 'readonly' => true], + 'billing_bill_created_at' => ['name' => 'billing_bill_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt', 'readonly' => true], + 'billing_bill_unit' => ['name' => 'billing_bill_unit', 'type' => 'int', 'internal' => 'unit'], ]; /** @@ -104,12 +112,12 @@ class BillMapper extends DataMapperFactory */ public const HAS_MANY = [ 'elements' => [ - 'mapper' => BillElementMapper::class, - 'table' => 'billing_bill_element', - 'self' => 'billing_bill_element_bill', - 'external' => null, + 'mapper' => BillElementMapper::class, + 'table' => 'billing_bill_element', + 'self' => 'billing_bill_element_bill', + 'external' => null, ], - 'files' => [ + 'files' => [ 'mapper' => MediaMapper::class, 'table' => 'billing_bill_media', 'external' => 'billing_bill_media_dst', @@ -130,17 +138,17 @@ class BillMapper extends DataMapperFactory * @since 1.0.0 */ public const OWNS_ONE = [ - 'type' => [ - 'mapper' => BillTypeMapper::class, - 'external' => 'billing_bill_type', + 'type' => [ + 'mapper' => BillTypeMapper::class, + 'external' => 'billing_bill_type', ], - 'referral' => [ - 'mapper' => AccountMapper::class, - 'external' => 'billing_bill_referral', + 'referral' => [ + 'mapper' => AccountMapper::class, + 'external' => 'billing_bill_referral', ], - 'template' => [ - 'mapper' => CollectionMapper::class, - 'external' => 'billing_bill_template', + 'template' => [ + 'mapper' => CollectionMapper::class, + 'external' => 'billing_bill_template', ], ]; @@ -152,16 +160,16 @@ class BillMapper extends DataMapperFactory */ public const BELONGS_TO = [ 'createdBy' => [ - 'mapper' => AccountMapper::class, - 'external' => 'billing_bill_created_by', + 'mapper' => AccountMapper::class, + 'external' => 'billing_bill_created_by', ], 'client' => [ - 'mapper' => ClientMapper::class, - 'external' => 'billing_bill_client', + 'mapper' => ClientMapper::class, + 'external' => 'billing_bill_client', ], 'supplier' => [ - 'mapper' => SupplierMapper::class, - 'external' => 'billing_bill_supplier', + 'mapper' => SupplierMapper::class, + 'external' => 'billing_bill_supplier', ], 'attributes' => [ 'mapper' => BillAttributeMapper::class, diff --git a/Models/BillType.php b/Models/BillType.php index 10e0574..93a4484 100755 --- a/Models/BillType.php +++ b/Models/BillType.php @@ -50,6 +50,8 @@ class BillType implements \JsonSerializable public bool $transferStock = true; + public bool $isAccounting = false; + public int $sign = 1; /** @@ -88,13 +90,13 @@ class BillType implements \JsonSerializable if ($l11n instanceof BaseStringL11n) { $this->l11n = $l11n; } elseif (isset($this->l11n) && $this->l11n instanceof BaseStringL11n) { - $this->l11n->content = $l11n; - $this->l11n->setLanguage($lang); + $this->l11n->content = $l11n; + $this->l11n->language = $lang; } else { - $this->l11n = new BaseStringL11n(); - $this->l11n->content = $l11n; - $this->l11n->ref = $this->id; - $this->l11n->setLanguage($lang); + $this->l11n = new BaseStringL11n(); + $this->l11n->content = $l11n; + $this->l11n->ref = $this->id; + $this->l11n->language = $lang; } } @@ -144,9 +146,9 @@ class BillType implements \JsonSerializable public function toArray() : array { return [ - 'id' => $this->id, - 'numberFormat' => $this->numberFormat, - 'transferType' => $this->transferType, + 'id' => $this->id, + 'numberFormat' => $this->numberFormat, + 'transferType' => $this->transferType, ]; } diff --git a/Models/BillTypeL11nMapper.php b/Models/BillTypeL11nMapper.php index 635d43e..be5b1d6 100755 --- a/Models/BillTypeL11nMapper.php +++ b/Models/BillTypeL11nMapper.php @@ -37,10 +37,10 @@ final class BillTypeL11nMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_type_l11n_id' => ['name' => 'billing_type_l11n_id', 'type' => 'int', 'internal' => 'id'], - 'billing_type_l11n_name' => ['name' => 'billing_type_l11n_name', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], - 'billing_type_l11n_type' => ['name' => 'billing_type_l11n_type', 'type' => 'int', 'internal' => 'ref'], - 'billing_type_l11n_language' => ['name' => 'billing_type_l11n_language', 'type' => 'string', 'internal' => 'language'], + 'billing_type_l11n_id' => ['name' => 'billing_type_l11n_id', 'type' => 'int', 'internal' => 'id'], + 'billing_type_l11n_name' => ['name' => 'billing_type_l11n_name', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], + 'billing_type_l11n_type' => ['name' => 'billing_type_l11n_type', 'type' => 'int', 'internal' => 'ref'], + 'billing_type_l11n_language' => ['name' => 'billing_type_l11n_language', 'type' => 'string', 'internal' => 'language'], ]; /** diff --git a/Models/BillTypeMapper.php b/Models/BillTypeMapper.php index c147cbc..a3ec65c 100755 --- a/Models/BillTypeMapper.php +++ b/Models/BillTypeMapper.php @@ -37,15 +37,16 @@ final class BillTypeMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_type_id' => ['name' => 'billing_type_id', 'type' => 'int', 'internal' => 'id'], - 'billing_type_name' => ['name' => 'billing_type_name', 'type' => 'string', 'internal' => 'name'], - 'billing_type_number_format' => ['name' => 'billing_type_number_format', 'type' => 'string', 'internal' => 'numberFormat'], - 'billing_type_account_format' => ['name' => 'billing_type_account_format', 'type' => 'string', 'internal' => 'accountFormat'], - 'billing_type_transfer_type' => ['name' => 'billing_type_transfer_type', 'type' => 'int', 'internal' => 'transferType'], - '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_transfer_sign' => ['name' => 'billing_type_transfer_sign', 'type' => 'int', 'internal' => 'sign'], - 'billing_type_is_template' => ['name' => 'billing_type_is_template', 'type' => 'bool', 'internal' => 'isTemplate'], + 'billing_type_id' => ['name' => 'billing_type_id', 'type' => 'int', 'internal' => 'id'], + 'billing_type_name' => ['name' => 'billing_type_name', 'type' => 'string', 'internal' => 'name'], + 'billing_type_number_format' => ['name' => 'billing_type_number_format', 'type' => 'string', 'internal' => 'numberFormat'], + 'billing_type_account_format' => ['name' => 'billing_type_account_format', 'type' => 'string', 'internal' => 'accountFormat'], + 'billing_type_transfer_type' => ['name' => 'billing_type_transfer_type', 'type' => 'int', 'internal' => 'transferType'], + '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_is_template' => ['name' => 'billing_type_is_template', 'type' => 'bool', 'internal' => 'isTemplate'], ]; /** @@ -56,13 +57,13 @@ final class BillTypeMapper extends DataMapperFactory */ public const HAS_MANY = [ 'l11n' => [ - 'mapper' => BillTypeL11nMapper::class, - 'table' => 'billing_type_l11n', - 'self' => 'billing_type_l11n_type', - 'column' => 'content', - 'external' => null, + 'mapper' => BillTypeL11nMapper::class, + 'table' => 'billing_type_l11n', + 'self' => 'billing_type_l11n_type', + 'column' => 'content', + 'external' => null, ], - 'templates' => [ + 'templates' => [ 'mapper' => CollectionMapper::class, 'table' => 'billing_bill_type_media_rel', 'external' => 'billing_bill_type_media_rel_dst', @@ -77,9 +78,9 @@ final class BillTypeMapper extends DataMapperFactory * @since 1.0.0 */ public const OWNS_ONE = [ - 'defaultTemplate' => [ - 'mapper' => CollectionMapper::class, - 'external' => 'billing_type_default_template', + 'defaultTemplate' => [ + 'mapper' => CollectionMapper::class, + 'external' => 'billing_type_default_template', ], ]; diff --git a/Models/PaymentTermL11nMapper.php b/Models/PaymentTermL11nMapper.php index 5513e02..3510b12 100644 --- a/Models/PaymentTermL11nMapper.php +++ b/Models/PaymentTermL11nMapper.php @@ -37,10 +37,10 @@ final class PaymentTermL11nMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_payment_term_l11n_id' => ['name' => 'billing_payment_term_l11n_id', 'type' => 'int', 'internal' => 'id'], - 'billing_payment_term_l11n_name' => ['name' => 'billing_payment_term_l11n_name', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], - 'billing_payment_term_l11n_term' => ['name' => 'billing_payment_term_l11n_term', 'type' => 'int', 'internal' => 'ref'], - 'billing_payment_term_l11n_language' => ['name' => 'billing_payment_term_l11n_language', 'type' => 'string', 'internal' => 'language'], + 'billing_payment_term_l11n_id' => ['name' => 'billing_payment_term_l11n_id', 'type' => 'int', 'internal' => 'id'], + 'billing_payment_term_l11n_name' => ['name' => 'billing_payment_term_l11n_name', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], + 'billing_payment_term_l11n_term' => ['name' => 'billing_payment_term_l11n_term', 'type' => 'int', 'internal' => 'ref'], + 'billing_payment_term_l11n_language' => ['name' => 'billing_payment_term_l11n_language', 'type' => 'string', 'internal' => 'language'], ]; /** diff --git a/Models/PaymentTermMapper.php b/Models/PaymentTermMapper.php index 53aa26a..a8fcce5 100644 --- a/Models/PaymentTermMapper.php +++ b/Models/PaymentTermMapper.php @@ -37,8 +37,8 @@ final class PaymentTermMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_payment_term_id' => ['name' => 'billing_payment_term_id', 'type' => 'int', 'internal' => 'id'], - 'billing_payment_term_code' => ['name' => 'billing_payment_term_code', 'type' => 'string', 'internal' => 'title', 'autocomplete' => true], + 'billing_payment_term_id' => ['name' => 'billing_payment_term_id', 'type' => 'int', 'internal' => 'id'], + 'billing_payment_term_code' => ['name' => 'billing_payment_term_code', 'type' => 'string', 'internal' => 'title', 'autocomplete' => true], ]; /** @@ -54,7 +54,7 @@ final class PaymentTermMapper extends DataMapperFactory 'self' => 'billing_payment_term_l11n_term', 'column' => 'content', 'external' => null, - ] + ], ]; /** diff --git a/Models/Price/Price.php b/Models/Price/Price.php index 9a61c01..472bd0e 100755 --- a/Models/Price/Price.php +++ b/Models/Price/Price.php @@ -50,6 +50,7 @@ class Price implements \JsonSerializable public Item $item; public AttributeValue $itemsalesgroup; + public AttributeValue $itemproductgroup; public AttributeValue $itemsegment; @@ -76,20 +77,22 @@ class Price implements \JsonSerializable public int $type = PriceType::SALES; - public int $quantity = 0; + public FloatInt $quantity; public FloatInt $price; - public int $priceNew = 0; + public FloatInt $priceNew; - public int $discount = 0; + public FloatInt $discount; - public int $discountPercentage = 0; + public FloatInt $discountPercentage; - public int $bonus = 0; + public FloatInt $bonus; public bool $multiply = false; + public bool $isAdditive = false; + public string $currency = ISO4217CharEnum::_EUR; public ?\DateTime $start = null; @@ -103,12 +106,12 @@ class Price implements \JsonSerializable */ public function __construct() { - $this->item = new NullItem(); + $this->item = new NullItem(); $this->itemsalesgroup = new NullAttributeValue(); - $this->itemproductgroup = new NullAttributeValue(); - $this->itemsegment = new NullAttributeValue(); - $this->itemsection = new NullAttributeValue(); - $this->itemtype = new NullAttributeValue(); + $this->itemproductgroup = new NullAttributeValue(); + $this->itemsegment = new NullAttributeValue(); + $this->itemsection = new NullAttributeValue(); + $this->itemtype = new NullAttributeValue(); $this->client = new NullClient(); $this->clientgroup = new NullAttributeValue(); @@ -118,19 +121,12 @@ class Price implements \JsonSerializable $this->supplier = new NullSupplier(); - $this->price = new FloatInt(); - } - - /** - * Get id. - * - * @return int Model id - * - * @since 1.0.0 - */ - public function getId() : int - { - return $this->id; + $this->price = new FloatInt(); + $this->quantity = new FloatInt(); + $this->priceNew = new FloatInt(); + $this->discount = new FloatInt(); + $this->discountPercentage = new FloatInt(); + $this->bonus = new FloatInt(); } /** diff --git a/Models/Price/PriceMapper.php b/Models/Price/PriceMapper.php index 5108e5e..4c7dfc8 100755 --- a/Models/Price/PriceMapper.php +++ b/Models/Price/PriceMapper.php @@ -42,34 +42,34 @@ final class PriceMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_price_id' => ['name' => 'billing_price_id', 'type' => 'int', 'internal' => 'id'], - 'billing_price_name' => ['name' => 'billing_price_name', 'type' => 'string', 'internal' => 'name'], - 'billing_price_promocode' => ['name' => 'billing_price_promocode', 'type' => 'string', 'internal' => 'promocode'], - 'billing_price_item' => ['name' => 'billing_price_item', 'type' => 'int', 'internal' => 'item'], - 'billing_price_itemsegment' => ['name' => 'billing_price_itemsegment', 'type' => 'int', 'internal' => 'itemsegment'], - 'billing_price_itemsection' => ['name' => 'billing_price_itemsection', 'type' => 'int', 'internal' => 'itemsection'], - 'billing_price_itemsalesgroup' => ['name' => 'billing_price_itemsalesgroup', 'type' => 'int', 'internal' => 'itemsalesgroup'], - 'billing_price_itemproductgroup' => ['name' => 'billing_price_itemproductgroup', 'type' => 'int', 'internal' => 'itemproductgroup'], - 'billing_price_itemtype' => ['name' => 'billing_price_itemtype', 'type' => 'int', 'internal' => 'itemtype'], - 'billing_price_client' => ['name' => 'billing_price_client', 'type' => 'int', 'internal' => 'client'], - 'billing_price_clientsegment' => ['name' => 'billing_price_clientsegment', 'type' => 'int', 'internal' => 'clientsegment'], - 'billing_price_clientsection' => ['name' => 'billing_price_clientsection', 'type' => 'int', 'internal' => 'clientsection'], - 'billing_price_clientgroup' => ['name' => 'billing_price_clientgroup', 'type' => 'int', 'internal' => 'clientgroup'], - 'billing_price_clienttype' => ['name' => 'billing_price_clienttype', 'type' => 'int', 'internal' => 'clienttype'], - 'billing_price_clientcountry' => ['name' => 'billing_price_clientcountry', 'type' => 'string', 'internal' => 'clientcountry'], - 'billing_price_supplier' => ['name' => 'billing_price_supplier', 'type' => 'int', 'internal' => 'supplier'], - 'billing_price_unit' => ['name' => 'billing_price_unit', 'type' => 'int', 'internal' => 'unit'], - 'billing_price_type' => ['name' => 'billing_price_type', 'type' => 'int', 'internal' => 'type'], - 'billing_price_quantity' => ['name' => 'billing_price_quantity', 'type' => 'int', 'internal' => 'quantity'], - 'billing_price_price' => ['name' => 'billing_price_price', 'type' => 'Serializable', 'internal' => 'price'], - 'billing_price_price_new' => ['name' => 'billing_price_price_new', 'type' => 'int', 'internal' => 'priceNew'], - 'billing_price_discount' => ['name' => 'billing_price_discount', 'type' => 'int', 'internal' => 'discount'], - 'billing_price_discountp' => ['name' => 'billing_price_discountp', 'type' => 'int', 'internal' => 'discountPercentage'], - 'billing_price_bonus' => ['name' => 'billing_price_bonus', 'type' => 'int', 'internal' => 'bonus'], - 'billing_price_multiply' => ['name' => 'billing_price_multiply', 'type' => 'bool', 'internal' => 'multiply'], - 'billing_price_currency' => ['name' => 'billing_price_currency', 'type' => 'string', 'internal' => 'currency'], - 'billing_price_start' => ['name' => 'billing_price_start', 'type' => 'DateTime', 'internal' => 'start'], - 'billing_price_end' => ['name' => 'billing_price_end', 'type' => 'DateTime', 'internal' => 'end'], + 'billing_price_id' => ['name' => 'billing_price_id', 'type' => 'int', 'internal' => 'id'], + 'billing_price_name' => ['name' => 'billing_price_name', 'type' => 'string', 'internal' => 'name'], + 'billing_price_promocode' => ['name' => 'billing_price_promocode', 'type' => 'string', 'internal' => 'promocode'], + 'billing_price_item' => ['name' => 'billing_price_item', 'type' => 'int', 'internal' => 'item'], + 'billing_price_itemsegment' => ['name' => 'billing_price_itemsegment', 'type' => 'int', 'internal' => 'itemsegment'], + 'billing_price_itemsection' => ['name' => 'billing_price_itemsection', 'type' => 'int', 'internal' => 'itemsection'], + 'billing_price_itemsalesgroup' => ['name' => 'billing_price_itemsalesgroup', 'type' => 'int', 'internal' => 'itemsalesgroup'], + 'billing_price_itemproductgroup' => ['name' => 'billing_price_itemproductgroup', 'type' => 'int', 'internal' => 'itemproductgroup'], + 'billing_price_itemtype' => ['name' => 'billing_price_itemtype', 'type' => 'int', 'internal' => 'itemtype'], + 'billing_price_client' => ['name' => 'billing_price_client', 'type' => 'int', 'internal' => 'client'], + 'billing_price_clientsegment' => ['name' => 'billing_price_clientsegment', 'type' => 'int', 'internal' => 'clientsegment'], + 'billing_price_clientsection' => ['name' => 'billing_price_clientsection', 'type' => 'int', 'internal' => 'clientsection'], + 'billing_price_clientgroup' => ['name' => 'billing_price_clientgroup', 'type' => 'int', 'internal' => 'clientgroup'], + 'billing_price_clienttype' => ['name' => 'billing_price_clienttype', 'type' => 'int', 'internal' => 'clienttype'], + 'billing_price_clientcountry' => ['name' => 'billing_price_clientcountry', 'type' => 'string', 'internal' => 'clientcountry'], + 'billing_price_supplier' => ['name' => 'billing_price_supplier', 'type' => 'int', 'internal' => 'supplier'], + 'billing_price_unit' => ['name' => 'billing_price_unit', 'type' => 'int', 'internal' => 'unit'], + 'billing_price_type' => ['name' => 'billing_price_type', 'type' => 'int', 'internal' => 'type'], + 'billing_price_quantity' => ['name' => 'billing_price_quantity', 'type' => 'Serializable', 'internal' => 'quantity'], + 'billing_price_price' => ['name' => 'billing_price_price', 'type' => 'Serializable', 'internal' => 'price'], + 'billing_price_price_new' => ['name' => 'billing_price_price_new', 'type' => 'Serializable', 'internal' => 'priceNew'], + 'billing_price_discount' => ['name' => 'billing_price_discount', 'type' => 'Serializable', 'internal' => 'discount'], + 'billing_price_discountp' => ['name' => 'billing_price_discountp', 'type' => 'Serializable', 'internal' => 'discountPercentage'], + 'billing_price_bonus' => ['name' => 'billing_price_bonus', 'type' => 'Serializable', 'internal' => 'bonus'], + 'billing_price_multiply' => ['name' => 'billing_price_multiply', 'type' => 'bool', 'internal' => 'multiply'], + 'billing_price_currency' => ['name' => 'billing_price_currency', 'type' => 'string', 'internal' => 'currency'], + 'billing_price_start' => ['name' => 'billing_price_start', 'type' => 'DateTime', 'internal' => 'start'], + 'billing_price_end' => ['name' => 'billing_price_end', 'type' => 'DateTime', 'internal' => 'end'], ]; /** diff --git a/Models/PurchaseBillMapper.php b/Models/PurchaseBillMapper.php index 4ac320b..b6bf6a8 100755 --- a/Models/PurchaseBillMapper.php +++ b/Models/PurchaseBillMapper.php @@ -42,10 +42,10 @@ final class PurchaseBillMapper extends BillMapper */ public static function getPurchaseBeforePivot( mixed $pivot, - string $column = null, + ?string $column = null, int $limit = 50, int $depth = 3, - Builder $query = null + ?Builder $query = null ) : array { return self::getAll() @@ -61,10 +61,10 @@ final class PurchaseBillMapper extends BillMapper */ public static function getPurchaseAfterPivot( mixed $pivot, - string $column = null, + ?string $column = null, int $limit = 50, int $depth = 3, - Builder $query = null + ?Builder $query = null ) : array { return self::getAll() diff --git a/Models/SalesBillMapper.php b/Models/SalesBillMapper.php index 4ee9589..1271c80 100755 --- a/Models/SalesBillMapper.php +++ b/Models/SalesBillMapper.php @@ -42,10 +42,10 @@ final class SalesBillMapper extends BillMapper */ public static function getSalesBeforePivot( mixed $pivot, - string $column = null, + ?string $column = null, int $limit = 50, int $depth = 3, - Builder $query = null + ?Builder $query = null ) : array { return self::getAll() @@ -61,10 +61,10 @@ final class SalesBillMapper extends BillMapper */ public static function getSalesAfterPivot( mixed $pivot, - string $column = null, + ?string $column = null, int $limit = 50, int $depth = 3, - Builder $query = null + ?Builder $query = null ) : array { return self::getAll() @@ -132,7 +132,7 @@ final class SalesBillMapper extends BillMapper AND billing_bill_performance_date <= '{$end->format('Y-m-d H:i:s')}'; SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return isset($result[0]['net_count']) @@ -380,7 +380,7 @@ final class SalesBillMapper extends BillMapper ORDER BY year ASC, month ASC; SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return $result ?? []; @@ -406,7 +406,7 @@ final class SalesBillMapper extends BillMapper ORDER BY year ASC, month ASC; SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return $result ?? []; @@ -424,7 +424,7 @@ final class SalesBillMapper extends BillMapper AND billing_bill_performance_date <= '{$end->format('Y-m-d H:i:s')}'; SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return new FloatInt((int) ($result[0]['net_sales'] ?? 0)); @@ -439,7 +439,7 @@ final class SalesBillMapper extends BillMapper WHERE billing_bill_element_item = {$item} SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return new FloatInt((int) ($result[0]['net_sales'] ?? 0)); @@ -461,7 +461,7 @@ final class SalesBillMapper extends BillMapper LIMIT 1; SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return isset($result[0]['billing_bill_created_at']) @@ -480,7 +480,7 @@ final class SalesBillMapper extends BillMapper AND billing_bill_performance_date <= '{$end->format('Y-m-d H:i:s')}'; SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return new FloatInt((int) ($result[0]['net_sales'] ?? 0)); @@ -494,7 +494,7 @@ final class SalesBillMapper extends BillMapper WHERE billing_bill_client = {$client}; SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return new FloatInt((int) ($result[0]['net_sales'] ?? 0)); @@ -515,7 +515,7 @@ final class SalesBillMapper extends BillMapper LIMIT 1; SQL; - $query = new Builder(self::$db); + $query = new Builder(self::$db); $result = $query->raw($sql)->execute()->fetchAll(\PDO::FETCH_ASSOC); return isset($result[0]['billing_bill_created_at']) diff --git a/Models/ShippingTermL11nMapper.php b/Models/ShippingTermL11nMapper.php index be630b4..e67686a 100644 --- a/Models/ShippingTermL11nMapper.php +++ b/Models/ShippingTermL11nMapper.php @@ -37,10 +37,10 @@ final class ShippingTermL11nMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_shipping_term_l11n_id' => ['name' => 'billing_shipping_term_l11n_id', 'type' => 'int', 'internal' => 'id'], - 'billing_shipping_term_l11n_name' => ['name' => 'billing_shipping_term_l11n_name', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], - 'billing_shipping_term_l11n_term' => ['name' => 'billing_shipping_term_l11n_term', 'type' => 'int', 'internal' => 'ref'], - 'billing_shipping_term_l11n_language' => ['name' => 'billing_shipping_term_l11n_language', 'type' => 'string', 'internal' => 'language'], + 'billing_shipping_term_l11n_id' => ['name' => 'billing_shipping_term_l11n_id', 'type' => 'int', 'internal' => 'id'], + 'billing_shipping_term_l11n_name' => ['name' => 'billing_shipping_term_l11n_name', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], + 'billing_shipping_term_l11n_term' => ['name' => 'billing_shipping_term_l11n_term', 'type' => 'int', 'internal' => 'ref'], + 'billing_shipping_term_l11n_language' => ['name' => 'billing_shipping_term_l11n_language', 'type' => 'string', 'internal' => 'language'], ]; /** diff --git a/Models/ShippingTermMapper.php b/Models/ShippingTermMapper.php index 791027b..5b55f9c 100644 --- a/Models/ShippingTermMapper.php +++ b/Models/ShippingTermMapper.php @@ -37,8 +37,8 @@ final class ShippingTermMapper extends DataMapperFactory * @since 1.0.0 */ public const COLUMNS = [ - 'billing_shipping_term_id' => ['name' => 'billing_shipping_term_id', 'type' => 'int', 'internal' => 'id'], - 'billing_shipping_term_code' => ['name' => 'billing_shipping_term_code', 'type' => 'string', 'internal' => 'title', 'autocomplete' => true], + 'billing_shipping_term_id' => ['name' => 'billing_shipping_term_id', 'type' => 'int', 'internal' => 'id'], + 'billing_shipping_term_code' => ['name' => 'billing_shipping_term_code', 'type' => 'string', 'internal' => 'title', 'autocomplete' => true], ]; /** @@ -54,7 +54,7 @@ final class ShippingTermMapper extends DataMapperFactory 'self' => 'billing_shipping_term_l11n_term', 'column' => 'content', 'external' => null, - ] + ], ]; /** diff --git a/Models/StockBillMapper.php b/Models/StockBillMapper.php index a11467e..eda9236 100755 --- a/Models/StockBillMapper.php +++ b/Models/StockBillMapper.php @@ -39,10 +39,10 @@ final class StockBillMapper extends BillMapper */ public static function getStockBeforePivot( mixed $pivot, - string $column = null, + ?string $column = null, int $limit = 50, int $depth = 3, - Builder $query = null + ?Builder $query = null ) : array { return self::getAll() @@ -58,10 +58,10 @@ final class StockBillMapper extends BillMapper */ public static function getStockAfterPivot( mixed $pivot, - string $column = null, + ?string $column = null, int $limit = 50, int $depth = 3, - Builder $query = null + ?Builder $query = null ) : array { return self::getAll() diff --git a/Models/Subscription.php b/Models/Subscription.php index e704a6d..feccea0 100755 --- a/Models/Subscription.php +++ b/Models/Subscription.php @@ -50,7 +50,7 @@ class Subscription implements \JsonSerializable public int $client = 0; - public int $quantity = 0; + public FloatInt $quantity; /** * Constructor. @@ -59,19 +59,8 @@ class Subscription implements \JsonSerializable */ public function __construct() { - $price = new FloatInt(); - } - - /** - * Get id. - * - * @return int Model id - * - * @since 1.0.0 - */ - public function getId() : int - { - return $this->id; + $price = new FloatInt(); + $quantity = new FloatInt(); } /** @@ -80,7 +69,7 @@ class Subscription implements \JsonSerializable public function toArray() : array { return [ - 'id' => $this->id, + 'id' => $this->id, ]; } diff --git a/Models/SubscriptionMapper.php b/Models/SubscriptionMapper.php index 65fba7d..7f2a098 100755 --- a/Models/SubscriptionMapper.php +++ b/Models/SubscriptionMapper.php @@ -41,7 +41,7 @@ final class SubscriptionMapper extends DataMapperFactory 'billing_subscription_start' => ['name' => 'billing_subscription_start', 'type' => 'DateTime', 'internal' => 'start'], 'billing_subscription_end' => ['name' => 'billing_subscription_end', 'type' => 'DateTime', 'internal' => 'end'], 'billing_subscription_price' => ['name' => 'billing_subscription_price', 'type' => 'Serializable', 'internal' => 'price'], - 'billing_subscription_quantity' => ['name' => 'billing_subscription_quantity', 'type' => 'int', 'internal' => 'quantity'], + 'billing_subscription_quantity' => ['name' => 'billing_subscription_quantity', 'type' => 'Serializable', 'internal' => 'quantity'], 'billing_subscription_bill' => ['name' => 'billing_subscription_bill', 'type' => 'int', 'internal' => 'bill'], 'billing_subscription_item' => ['name' => 'billing_subscription_item', 'type' => 'int', 'internal' => 'item'], 'billing_subscription_autorenew' => ['name' => 'billing_subscription_autorenew', 'type' => 'bool', 'internal' => 'autoRenew'], diff --git a/Models/Tax/NullTaxCombination.php b/Models/Tax/NullTaxCombination.php index 5513ab2..13a142b 100755 --- a/Models/Tax/NullTaxCombination.php +++ b/Models/Tax/NullTaxCombination.php @@ -34,6 +34,7 @@ final class NullTaxCombination extends TaxCombination public function __construct(int $id = 0) { $this->id = $id; + parent::__construct(); } /** diff --git a/Models/Tax/TaxCombination.php b/Models/Tax/TaxCombination.php index 8425715..7debae1 100755 --- a/Models/Tax/TaxCombination.php +++ b/Models/Tax/TaxCombination.php @@ -16,6 +16,8 @@ namespace Modules\Billing\Models\Tax; use Modules\Attribute\Models\AttributeValue; use Modules\Attribute\Models\NullAttributeValue; +use Modules\Finance\Models\NullTaxCode; +use Modules\Finance\Models\TaxCode; /** * Billing class. @@ -41,9 +43,7 @@ class TaxCombination implements \JsonSerializable public AttributeValue $itemCode; - public string $taxCode = ''; - - // @todo consider to add the tax code object directly, it is annoying to make a manuall mapper call which is often required afterwards. + public TaxCode $taxCode; public int $taxType = BillTaxType::SALES; @@ -69,6 +69,7 @@ class TaxCombination implements \JsonSerializable public function __construct() { $this->itemCode = new NullAttributeValue(); + $this->taxCode = new NullTaxCode(); } /** @@ -77,7 +78,7 @@ class TaxCombination implements \JsonSerializable public function toArray() : array { return [ - 'id' => $this->id, + 'id' => $this->id, ]; } diff --git a/Models/Tax/TaxCombinationMapper.php b/Models/Tax/TaxCombinationMapper.php index e59fe9f..4d498a7 100755 --- a/Models/Tax/TaxCombinationMapper.php +++ b/Models/Tax/TaxCombinationMapper.php @@ -15,8 +15,9 @@ declare(strict_types=1); namespace Modules\Billing\Models\Tax; use Modules\ClientManagement\Models\Attribute\ClientAttributeValueMapper; +use Modules\Finance\Models\TaxCodeMapper; use Modules\ItemManagement\Models\Attribute\ItemAttributeValueMapper; -use Modules\SupplierManagement\Models\SupplierAttributeValueMapper; +use Modules\SupplierManagement\Models\Attribute\SupplierAttributeValueMapper; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; /** @@ -73,6 +74,12 @@ final class TaxCombinationMapper extends DataMapperFactory 'mapper' => ItemAttributeValueMapper::class, 'external' => 'billing_tax_item_code', ], + 'taxCode' => [ + 'mapper' => TaxCodeMapper::class, + 'external' => 'billing_tax_code', + 'by' => 'abbr', + 'column' => 'abbr', + ], ]; /** diff --git a/Theme/Backend/Lang/Navigation.en.lang.php b/Theme/Backend/Lang/Navigation.en.lang.php index 35023be..0d87942 100755 --- a/Theme/Backend/Lang/Navigation.en.lang.php +++ b/Theme/Backend/Lang/Navigation.en.lang.php @@ -19,6 +19,6 @@ return ['Navigation' => [ 'InvoiceRecognition' => 'Invoice Recognition', 'Open' => 'Open', 'Upload' => 'Upload', - 'PaymentTerms' => 'Payment Terms', - 'ShippingTerms' => 'Shipping Terms', + 'PaymentTerms' => 'Payment Terms', + 'ShippingTerms' => 'Shipping Terms', ]]; diff --git a/Theme/Backend/Lang/de.lang.php b/Theme/Backend/Lang/de.lang.php index b90f441..9be6a93 100755 --- a/Theme/Backend/Lang/de.lang.php +++ b/Theme/Backend/Lang/de.lang.php @@ -40,15 +40,15 @@ return ['Billing' => [ 'Due' => 'Fällig', 'Email' => 'Email', 'Freightage' => 'Fracht', - 'Gross' => 'Grob', + 'Gross' => 'Brutto', 'Invoice' => 'Rechnung', 'Item' => 'Artikel', 'Items' => 'Produkte', 'Language' => 'Sprache', 'Log' => 'Protokoll', - 'Logs' => 'Protokoll', + 'Logs' => 'Protokolle', 'Media' => 'Medien', - 'MoneyTransfer' => 'Geldüberweisung', + 'MoneyTransfer' => 'Überweisung', 'Name' => 'Name', 'Net' => 'Netz', 'Offer' => 'Angebot', @@ -59,7 +59,7 @@ return ['Billing' => [ 'Prepaid' => 'Vorausbezahlt', 'Preview' => 'Vorschau', 'Price' => 'Preis', - 'Profit' => 'Profitieren', + 'Profit' => 'Gewinn', 'Quantity' => 'Menge', 'Recipient' => 'Empfänger', 'Select' => 'Wählen', @@ -68,15 +68,17 @@ return ['Billing' => [ 'Supplier' => 'Anbieter', 'SupplierID' => 'Lieferanten ID', 'Tax' => 'Steuer', + 'TaxP' => 'Steuer %', + 'Margin' => 'Marge', 'TermsOfDelivery' => 'Lieferbedingungen', 'Total' => 'Gesamt', 'Type' => 'Typ', 'Types' => 'Typen', 'Upload' => 'Hochladen', 'Zip' => 'Zip', - 'Files' => 'Files', - 'PaymentTerms' => 'Zahlungsbedingungen', - 'ShippingTerms' => 'Lieferbedingungen', - 'PaymentTerm' => 'Zahlungsbedingung', - 'ShippingTerm' => 'Lieferbedingung', + 'Files' => 'Files', + 'PaymentTerms' => 'Zahlungsbedingungen', + 'ShippingTerms' => 'Lieferbedingungen', + 'PaymentTerm' => 'Zahlungsbedingung', + 'ShippingTerm' => 'Lieferbedingung', ]]; diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index 4288b03..e12411e 100755 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -68,15 +68,17 @@ return ['Billing' => [ 'Supplier' => 'Supplier', 'SupplierID' => 'Supplier ID', 'Tax' => 'Tax', + 'TaxP' => 'Tax %', + 'Margin' => 'Margin', 'TermsOfDelivery' => 'Terms Of Delivery', 'Total' => 'Total', 'Type' => 'Type', 'Types' => 'Types', 'Upload' => 'Upload', 'Zip' => 'Zip', - 'Files' => 'Files', - 'PaymentTerms' => 'Payment Terms', - 'ShippingTerms' => 'Shipping Terms', - 'PaymentTerm' => 'Payment Term', - 'ShippingTerm' => 'Shipping Term', + 'Files' => 'Files', + 'PaymentTerms' => 'Payment Terms', + 'ShippingTerms' => 'Shipping Terms', + 'PaymentTerm' => 'Payment Term', + 'ShippingTerm' => 'Shipping Term', ]]; diff --git a/Theme/Backend/bill-create.tpl.php b/Theme/Backend/bill-create.tpl.php index 65295d2..34841d6 100755 --- a/Theme/Backend/bill-create.tpl.php +++ b/Theme/Backend/bill-create.tpl.php @@ -20,10 +20,10 @@ use phpOMS\Localization\ISO4217Enum; use phpOMS\Localization\ISO639Enum; use phpOMS\Uri\UriFactory; -$countryCodes = ISO3166TwoEnum::getConstants(); -$countries = ISO3166NameEnum::getConstants(); -$languages = ISO639Enum::getConstants(); -$currencies = ISO4217Enum::getConstants(); +$countryCodes = ISO3166TwoEnum::getConstants(); +$countries = ISO3166NameEnum::getConstants(); +$languages = ISO639Enum::getConstants(); +$currencies = ISO4217Enum::getConstants(); /** * @var \phpOMS\Views\View $this @@ -32,7 +32,7 @@ $media = $this->data['media'] ?? []; /** @var \Modules\Billing\Models\Bill $bill */ $bill = $this->getData('bill') ?? new NullBill(); -$elements = $bill->getElements(); +$elements = $bill->elements; $billTypes = $this->data['billtypes'] ?? []; @@ -41,7 +41,7 @@ $archive = $bill->getFileByTypeName('original'); /** @var \Modules\Auditor\Models\Audit */ $logs = $this->data['logs'] ?? []; -$editable = $bill->id === 0 || \in_array($bill->getStatus(), [BillStatus::DRAFT, BillStatus::UNPARSED]); +$editable = $bill->id === 0 || \in_array($bill->status, [BillStatus::DRAFT, BillStatus::UNPARSED]); $disabled = $editable ? '' : ' disabled'; $isNew = $archive->id === 0; @@ -82,7 +82,7 @@ echo $this->data['nav']->render(); ?> @@ -119,7 +119,7 @@ echo $this->data['nav']->render(); ?> client?->id ?? 0) > 0) : ?> @@ -284,13 +284,14 @@ echo $this->data['nav']->render(); ?> getHtml('Item'); ?> getHtml('Name'); ?> - getHtml('Quantity'); ?> + getHtml('Quantity'); ?> getHtml('Discount'); ?> getHtml('DiscountP'); ?> - getHtml('Bonus'); ?> - getHtml('Tax'); ?> + getHtml('Bonus'); ?> getHtml('Price'); ?> + getHtml('TaxP'); ?> getHtml('Net'); ?> + getHtml('Margin'); ?> @@ -321,31 +323,49 @@ echo $this->data['nav']->render(); ?> expand_more close - > - >itemName; ?> - > - > - > - > - > - > - getCurrency($element->totalSalesPriceNet); ?> + + > + + > + > + > + > + > + > + getCurrency($element->totalSalesPriceNet, symbol: ''); ?> + totalSalesPriceNet->value === 0 ? 0 : (1 - $element->totalPurchasePriceNet->value / $element->totalSalesPriceNet->value) * 100, 2); ?>% expand_less expand_more close - - - - - - - - + + + + + + + + + + + + + getHtml('Total'); ?> + + netDiscount->getAmount(2); ?> + netDiscount->value === 0 ? 0 : ($bill->netDiscount->value / ($bill->netSales->value + $bill->netDiscount->value)) * 100, 2); ?>% + + + taxP->getAmount(2); ?> + netSales->getAmount(2); ?> + netSales->value === 0 ? 0 : (1 - $bill->netCosts->value / $bill->netSales->value) * 100, 2); ?>% @@ -518,7 +538,7 @@ echo $this->data['nav']->render(); ?> id); + $url = UriFactory::build('{/base}/admin/audit/view?id=' . $audit->id); ?> id; ?> @@ -532,7 +552,7 @@ echo $this->data['nav']->render(); ?> href="createdBy->id); ?>">printHtml( $this->renderUserName('%3$s %2$s %1$s', [$audit->createdBy->name1, $audit->createdBy->name2, $audit->createdBy->name3, $audit->createdBy->login]) ); ?> - createdAt->format('Y-m-d'); ?> + createdAt->format('Y-m-d H:i'); ?> diff --git a/Theme/Backend/payment-view.tpl.php b/Theme/Backend/payment-view.tpl.php index 6453683..40f1511 100644 --- a/Theme/Backend/payment-view.tpl.php +++ b/Theme/Backend/payment-view.tpl.php @@ -15,7 +15,7 @@ declare(strict_types=1); use phpOMS\Uri\UriFactory; /** @var \phpOMS\Localization\BaseStringL11nType */ -$type = $this->data['type']; +$type = $this->data['type']; /** @var \phpOMS\Views\View $this */ echo $this->data['nav']->render(); ?> diff --git a/Theme/Backend/purchase-bill-list.tpl.php b/Theme/Backend/purchase-bill-list.tpl.php index 3569cde..5db7e5c 100755 --- a/Theme/Backend/purchase-bill-list.tpl.php +++ b/Theme/Backend/purchase-bill-list.tpl.php @@ -163,7 +163,7 @@ echo $this->data['nav']->render(); ?> getNumber(); ?> type->getL11n(); ?> - supplier->number; ?> + supplier->number; ?> printHtml($value->billTo); ?> billAddress; ?> diff --git a/Theme/Backend/purchase-bill.tpl.php b/Theme/Backend/purchase-bill.tpl.php index 04bea9a..c0ee51b 100755 --- a/Theme/Backend/purchase-bill.tpl.php +++ b/Theme/Backend/purchase-bill.tpl.php @@ -23,12 +23,12 @@ include __DIR__ . '/../../../Media/Theme/Backend/template-functions.php'; /** @var \Modules\Billing\Models\Bill $bill */ $bill = $this->data['bill']; -$elements = $bill->getElements(); +$elements = $bill->elements; $billTypes = $this->data['billtypes'] ?? []; $originalType = $this->data['originalType']; -$original = $bill->getFileByType($originalType); +$original = $bill->getFileByType($originalType); /** @var \Modules\Auditor\Models\Audit */ $logs = $this->data['logs'] ?? []; @@ -302,7 +302,7 @@ echo $this->data['nav']->render(); ?> id); + $url = UriFactory::build('{/base}/admin/audit/view?id=' . $audit->id); ?> id; ?> diff --git a/Theme/Backend/sales-bill-list.tpl.php b/Theme/Backend/sales-bill-list.tpl.php index 22a39af..61c72da 100755 --- a/Theme/Backend/sales-bill-list.tpl.php +++ b/Theme/Backend/sales-bill-list.tpl.php @@ -174,7 +174,7 @@ echo $this->data['nav']->render(); ?> getNumber(); ?> type->getL11n(); ?> - client->number; ?> + client->number; ?> printHtml($value->billTo); ?> billAddress; ?> diff --git a/Theme/Backend/shipping-view.tpl.php b/Theme/Backend/shipping-view.tpl.php index 025dd38..545385f 100644 --- a/Theme/Backend/shipping-view.tpl.php +++ b/Theme/Backend/shipping-view.tpl.php @@ -15,7 +15,7 @@ declare(strict_types=1); use phpOMS\Uri\UriFactory; /** @var \phpOMS\Localization\BaseStringL11nType */ -$type = $this->data['type']; +$type = $this->data['type']; /** @var \phpOMS\Views\View $this */ echo $this->data['nav']->render(); ?> diff --git a/Theme/Backend/user-purchase-bill-dashboard.tpl.php b/Theme/Backend/user-purchase-bill-dashboard.tpl.php index 1ad906e..2bc9062 100755 --- a/Theme/Backend/user-purchase-bill-dashboard.tpl.php +++ b/Theme/Backend/user-purchase-bill-dashboard.tpl.php @@ -165,7 +165,7 @@ echo $this->data['nav']->render(); ?> getNumber(); ?> type->getL11n(); ?> - supplier->number; ?> + supplier->number; ?> printHtml($value->billTo); ?> billAddress; ?> diff --git a/Theme/Backend/user-purchase-bill.tpl.php b/Theme/Backend/user-purchase-bill.tpl.php index 2c1a8bd..c32abe0 100755 --- a/Theme/Backend/user-purchase-bill.tpl.php +++ b/Theme/Backend/user-purchase-bill.tpl.php @@ -23,13 +23,13 @@ include __DIR__ . '/../../../Media/Theme/Backend/template-functions.php'; */ $bill = $this->data['bill']; -$elements = $bill->getElements(); +$elements = $bill->elements; $previewType = $this->data['previewType']; $originalType = $this->data['originalType']; $billPdf = $bill->getFileByType($previewType); $original = $bill->getFileByType($originalType); -$media = $bill->getMedia(); +$media = $bill->files; echo $this->data['nav']->render(); ?> @@ -298,7 +298,7 @@ echo $this->data['nav']->render(); ?> extension === 'collection' ? UriFactory::build('{/base}/media/list?path=' . \rtrim($file->getVirtualPath(), '/') . '/' . $file->name) - : UriFactory::build('{/base}/media/single?id=' . $file->id + : UriFactory::build('{/base}/media/view?id=' . $file->id . '&path={?path}' . ( $file->id === 0 ? '/' . $file->name diff --git a/tests/Autoloader.php b/tests/Autoloader.php index 96d8c02..561d764 100755 --- a/tests/Autoloader.php +++ b/tests/Autoloader.php @@ -75,8 +75,8 @@ final class Autoloader */ public static function defaultAutoloader(string $class) : void { - $class = \ltrim($class, '\\'); - $class = \strtr($class, '_\\', '//'); + $class = \ltrim($class, '\\'); + $class = \strtr($class, '_\\', '//'); if (\stripos($class, 'Web/Backend') !== false || \stripos($class, 'Web/Api') !== false) { $class = \is_dir(__DIR__ . '/Web') ? $class : \str_replace('Web/', 'MainRepository/Web/', $class); diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php index ddc049d..4bf4f19 100755 --- a/tests/Bootstrap.php +++ b/tests/Bootstrap.php @@ -1,4 +1,15 @@ [ + 'db' => [ 'core' => [ 'masters' => [ - 'admin' => [ + 'admin' => [ 'db' => 'mysql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '3306', /* db host port */ @@ -80,7 +91,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'insert' => [ + 'insert' => [ 'db' => 'mysql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '3306', /* db host port */ @@ -90,7 +101,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'select' => [ + 'select' => [ 'db' => 'mysql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '3306', /* db host port */ @@ -100,7 +111,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'update' => [ + 'update' => [ 'db' => 'mysql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '3306', /* db host port */ @@ -110,7 +121,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'delete' => [ + 'delete' => [ 'db' => 'mysql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '3306', /* db host port */ @@ -120,7 +131,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'schema' => [ + 'schema' => [ 'db' => 'mysql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '3306', /* db host port */ @@ -132,7 +143,7 @@ $CONFIG = [ ], ], 'postgresql' => [ - 'admin' => [ + 'admin' => [ 'db' => 'pgsql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '5432', /* db host port */ @@ -142,7 +153,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'insert' => [ + 'insert' => [ 'db' => 'pgsql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '5432', /* db host port */ @@ -152,7 +163,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'select' => [ + 'select' => [ 'db' => 'pgsql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '5432', /* db host port */ @@ -162,7 +173,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'update' => [ + 'update' => [ 'db' => 'pgsql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '5432', /* db host port */ @@ -172,7 +183,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'delete' => [ + 'delete' => [ 'db' => 'pgsql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '5432', /* db host port */ @@ -182,7 +193,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'schema' => [ + 'schema' => [ 'db' => 'pgsql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '5432', /* db host port */ @@ -194,37 +205,37 @@ $CONFIG = [ ], ], 'sqlite' => [ - 'admin' => [ + 'admin' => [ 'db' => 'sqlite', /* db type */ 'database' => __DIR__ . '/../Karaka/phpOMS/Localization/Defaults/localization.sqlite', /* db name */ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'insert' => [ + 'insert' => [ 'db' => 'sqlite', /* db type */ 'database' => __DIR__ . '/../Karaka/phpOMS/Localization/Defaults/localization.sqlite', /* db name */ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'select' => [ + 'select' => [ 'db' => 'sqlite', /* db type */ 'database' => __DIR__ . '/../Karaka/phpOMS/Localization/Defaults/localization.sqlite', /* db name */ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'update' => [ + 'update' => [ 'db' => 'sqlite', /* db type */ 'database' => __DIR__ . '/../Karaka/phpOMS/Localization/Defaults/localization.sqlite', /* db name */ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'delete' => [ + 'delete' => [ 'db' => 'sqlite', /* db type */ 'database' => __DIR__ . '/../Karaka/phpOMS/Localization/Defaults/localization.sqlite', /* db name */ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'schema' => [ + 'schema' => [ 'db' => 'sqlite', /* db type */ 'database' => __DIR__ . '/../Karaka/phpOMS/Localization/Defaults/localization.sqlite', /* db name */ 'weight' => 1000, /* db table prefix */ @@ -232,7 +243,7 @@ $CONFIG = [ ], ], 'mssql' => [ - 'admin' => [ + 'admin' => [ 'db' => 'mssql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '1433', /* db host port */ @@ -242,7 +253,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'insert' => [ + 'insert' => [ 'db' => 'mssql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '1433', /* db host port */ @@ -252,7 +263,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'select' => [ + 'select' => [ 'db' => 'mssql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '1433', /* db host port */ @@ -262,7 +273,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'update' => [ + 'update' => [ 'db' => 'mssql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '1433', /* db host port */ @@ -272,7 +283,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'delete' => [ + 'delete' => [ 'db' => 'mssql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '1433', /* db host port */ @@ -282,7 +293,7 @@ $CONFIG = [ 'weight' => 1000, /* db table prefix */ 'datetimeformat' => 'Y-m-d H:i:s', ], - 'schema' => [ + 'schema' => [ 'db' => 'mssql', /* db type */ 'host' => '127.0.0.1', /* db host address */ 'port' => '1433', /* db host port */ @@ -322,16 +333,16 @@ $CONFIG = [ 'password' => '123456', ], ], - 'log' => [ + 'log' => [ 'file' => [ 'path' => __DIR__ . '/Logs', ], ], - 'page' => [ + 'page' => [ 'root' => '/', 'https' => false, ], - 'app' => [ + 'app' => [ 'path' => __DIR__, 'default' => [ 'app' => 'Backend', @@ -350,7 +361,7 @@ $CONFIG = [ ], ], ], - 'socket' => [ + 'socket' => [ 'master' => [ 'host' => '127.0.0.1', 'limit' => 300, @@ -360,7 +371,7 @@ $CONFIG = [ 'language' => [ 'en', ], - 'apis' => [ + 'apis' => [ ], ]; diff --git a/tests/Controller/Api/ApiBillControllerTrait.php b/tests/Controller/Api/ApiBillControllerTrait.php index 9c7decd..a170765 100644 --- a/tests/Controller/Api/ApiBillControllerTrait.php +++ b/tests/Controller/Api/ApiBillControllerTrait.php @@ -17,7 +17,6 @@ namespace Modules\Billing\tests\Controller\Api; use phpOMS\Message\Http\HttpRequest; use phpOMS\Message\Http\HttpResponse; use phpOMS\Message\Http\RequestStatusCode; -use phpOMS\Uri\HttpUri; use phpOMS\Utils\RnG\DateTime; trait ApiBillControllerTrait @@ -25,7 +24,7 @@ trait ApiBillControllerTrait public function testBillCreate() : void { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; @@ -53,7 +52,7 @@ trait ApiBillControllerTrait public function testBillElementCreate() : void { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; @@ -73,7 +72,7 @@ trait ApiBillControllerTrait public function testBillArchiveCreate() : void { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; @@ -87,7 +86,7 @@ trait ApiBillControllerTrait public function testBillNoteCreate() : void { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = \mt_rand(2, 5); @@ -107,7 +106,7 @@ trait ApiBillControllerTrait public function testBillCreateInvalidData() : void { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('invalid', '1'); @@ -123,7 +122,7 @@ trait ApiBillControllerTrait public function testBillElementCreateInvalidData() : void { $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; $request->setData('invalid', '1'); diff --git a/tests/Controller/Api/ApiPurchaseControllerTrait.php b/tests/Controller/Api/ApiPurchaseControllerTrait.php index b995e60..f1aef72 100644 --- a/tests/Controller/Api/ApiPurchaseControllerTrait.php +++ b/tests/Controller/Api/ApiPurchaseControllerTrait.php @@ -18,7 +18,6 @@ use phpOMS\Message\Http\HttpRequest; use phpOMS\Message\Http\HttpResponse; use phpOMS\Message\Http\RequestStatusCode; use phpOMS\System\File\Local\Directory; -use phpOMS\Uri\HttpUri; use phpOMS\Utils\TestUtils; trait ApiPurchaseControllerTrait @@ -44,7 +43,7 @@ trait ApiPurchaseControllerTrait $file = $invoiceDocs[$i]; $response = new HttpResponse(); - $request = new HttpRequest(new HttpUri('')); + $request = new HttpRequest(); $request->header->account = 1; diff --git a/tests/Controller/ApiControllerTest.php b/tests/Controller/ApiControllerTest.php index 76644de..710f6de 100755 --- a/tests/Controller/ApiControllerTest.php +++ b/tests/Controller/ApiControllerTest.php @@ -59,14 +59,14 @@ final class ApiControllerTest extends \PHPUnit\Framework\TestCase protected string $appName = 'Api'; }; - $this->app->dbPool = $GLOBALS['dbpool']; - $this->app->unitId = 1; - $this->app->accountManager = new AccountManager($GLOBALS['session']); - $this->app->appSettings = new CoreSettings(); - $this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../../../Modules/'); - $this->app->dispatcher = new Dispatcher($this->app); - $this->app->eventManager = new EventManager($this->app->dispatcher); - $this->app->l11nManager = new L11nManager(); + $this->app->dbPool = $GLOBALS['dbpool']; + $this->app->unitId = 1; + $this->app->accountManager = new AccountManager($GLOBALS['session']); + $this->app->appSettings = new CoreSettings(); + $this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../../../Modules/'); + $this->app->dispatcher = new Dispatcher($this->app); + $this->app->eventManager = new EventManager($this->app->dispatcher); + $this->app->l11nManager = new L11nManager(); $this->app->eventManager->importFromFile(__DIR__ . '/../../../../Web/Api/Hooks.php'); $account = new Account(); diff --git a/tests/Models/BillTest.php b/tests/Models/BillTest.php index 374a70f..1851f5b 100755 --- a/tests/Models/BillTest.php +++ b/tests/Models/BillTest.php @@ -15,10 +15,7 @@ declare(strict_types=1); namespace Modules\Billing\tests\Models; use Modules\Billing\Models\Bill; -use Modules\Billing\Models\BillElement; -use Modules\Billing\Models\BillStatus; use Modules\Billing\Models\NullBillType; -use phpOMS\Localization\ISO4217CharEnum; /** * @internal @@ -51,8 +48,6 @@ final class BillTest extends \PHPUnit\Framework\TestCase self::assertNull($this->bill->send); self::assertNull($this->bill->client); self::assertNull($this->bill->supplier); - self::assertEquals([], $this->bill->getVouchers()); - self::assertEquals([], $this->bill->getTrackings()); self::assertInstanceOf('\Modules\Media\Models\NullMedia', $this->bill->getFileByType(0)); self::assertEquals('', $this->bill->shipTo); @@ -82,7 +77,7 @@ final class BillTest extends \PHPUnit\Framework\TestCase self::assertEquals('', $this->bill->paymentText); self::assertEquals(0, $this->bill->terms); self::assertEquals('', $this->bill->termsText); - self::assertEquals(0, $this->bill->shipping); + self::assertEquals(0, $this->bill->shippingTerms); self::assertEquals('', $this->bill->shippingText); } @@ -96,94 +91,44 @@ final class BillTest extends \PHPUnit\Framework\TestCase self::assertEquals(\date('Y') . \date('m') . \date('d') . '-0', $this->bill->getNumber()); } - /** - * @covers Modules\Billing\Models\Bill - * @group module - */ - public function testStatusInputOutput() : void - { - $this->bill->setStatus(BillStatus::ACTIVE); - self::assertEquals(BillStatus::ACTIVE, $this->bill->getStatus()); - } - - /** - * @covers Modules\Billing\Models\Bill - * @group module - */ - public function testCurrencyInputOutput() : void - { - $this->bill->setCurrency(ISO4217CharEnum::_USD); - self::assertEquals(ISO4217CharEnum::_USD, $this->bill->getCurrency()); - } - - /** - * @covers Modules\Billing\Models\Bill - * @group module - */ - public function testVoucherInputOutput() : void - { - $this->bill->addVoucher('TEST'); - self::assertEquals(['TEST'], $this->bill->getVouchers()); - } - - /** - * @covers Modules\Billing\Models\Bill - * @group module - */ - public function testTrackingInputOutput() : void - { - $this->bill->addTracking('TEST'); - self::assertEquals(['TEST'], $this->bill->getTrackings()); - } - - /** - * @covers Modules\Billing\Models\Bill - * @group module - */ - public function testElementInputOutput() : void - { - $this->bill->addElement(new BillElement()); - self::assertCount(1, $this->bill->getElements()); - } - /** * @covers Modules\Billing\Models\Bill * @group module */ public function testSerialize() : void { - $this->bill->number = '123456'; - $this->bill->type = new NullBillType(2); - $this->bill->shipTo = 'To'; - $this->bill->shipFAO = 'FAO'; - $this->bill->shipAddress = 'Address'; - $this->bill->shipCity = 'City'; - $this->bill->shipZip = 'Zip'; - $this->bill->shipCountry = 'Country'; - $this->bill->billTo = 'To'; - $this->bill->billFAO = 'FAO'; - $this->bill->billAddress = 'Address'; - $this->bill->billCity = 'City'; - $this->bill->billZip = 'Zip'; - $this->bill->billCountry = 'Country'; + $this->bill->number = '123456'; + $this->bill->type = new NullBillType(2); + $this->bill->shipTo = 'To'; + $this->bill->shipFAO = 'FAO'; + $this->bill->shipAddress = 'Address'; + $this->bill->shipCity = 'City'; + $this->bill->shipZip = 'Zip'; + $this->bill->shipCountry = 'Country'; + $this->bill->billTo = 'To'; + $this->bill->billFAO = 'FAO'; + $this->bill->billAddress = 'Address'; + $this->bill->billCity = 'City'; + $this->bill->billZip = 'Zip'; + $this->bill->billCountry = 'Country'; self::assertEquals( [ - 'id' => 0, - 'number' => '123456', - 'type' => $this->bill->type, - 'shipTo' => 'To', - 'shipFAO' => 'FAO', - 'shipAddress' => 'Address', - 'shipCity' => 'City', - 'shipZip' => 'Zip', - 'shipCountry' => 'Country', - 'billTo' => 'To', - 'billFAO' => 'FAO', - 'billAddress' => 'Address', - 'billCity' => 'City', - 'billZip' => 'Zip', - 'billCountry' => 'Country', + 'id' => 0, + 'number' => '123456', + 'type' => $this->bill->type, + 'shipTo' => 'To', + 'shipFAO' => 'FAO', + 'shipAddress' => 'Address', + 'shipCity' => 'City', + 'shipZip' => 'Zip', + 'shipCountry' => 'Country', + 'billTo' => 'To', + 'billFAO' => 'FAO', + 'billAddress' => 'Address', + 'billCity' => 'City', + 'billZip' => 'Zip', + 'billCountry' => 'Country', ], $this->bill->jsonSerialize() );