diff --git a/Admin/Install/Media.php b/Admin/Install/Media.php index c0617b5..1f62780 100755 --- a/Admin/Install/Media.php +++ b/Admin/Install/Media.php @@ -14,8 +14,6 @@ declare(strict_types=1); namespace Modules\Billing\Admin\Install; -use Model\Setting; -use Model\SettingMapper; use Modules\Billing\Models\SettingsEnum; use phpOMS\Application\ApplicationAbstract; @@ -43,29 +41,26 @@ class Media { $media = \Modules\Media\Admin\Installer::installExternal($app, ['path' => __DIR__ . '/Media.install.json']); - $preivewType = (int) \reset($media['type'][0]); - $originalType = (int) \reset($media['type'][1]); - - $setting = new Setting(); - SettingMapper::create()->execute( - $setting->with( - 0, - SettingsEnum::PREVIEW_MEDIA_TYPE, - (string) $preivewType, - '\\d+', - module: 'Billing' - ) - ); - - $setting = new Setting(); - SettingMapper::create()->execute( - $setting->with( - 0, - SettingsEnum::ORIGINAL_MEDIA_TYPE, - (string) $originalType, - '\\d+', - module: 'Billing' - ) + \Modules\Admin\Admin\Installer::installExternal( + $app, + [ + 'data' => [ + [ + 'type' => 'setting', + 'name' => SettingsEnum::PREVIEW_MEDIA_TYPE, + 'content' => (string) $media['type'][0]['id'], + 'pattern' => '\\d+', + 'module' => 'Billing' + ], + [ + 'type' => 'setting', + 'name' => SettingsEnum::ORIGINAL_MEDIA_TYPE, + 'content' => (string) $media['type'][1]['id'], + 'pattern' => '\\d+', + 'module' => 'Billing' + ] + ] + ] ); } } diff --git a/Admin/Install/db.json b/Admin/Install/db.json index ac20ad0..b30421e 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -388,6 +388,15 @@ "primary": true, "autoincrement": true }, + "billing_bill_sequence": { + "name": "billing_bill_sequence", + "type": "INT", + "null": false, + "multi_autoincrement": [ + "billing_bill_unit", + "billing_bill_type" + ] + }, "billing_bill_number": { "name": "billing_bill_number", "type": "VARCHAR(255)", @@ -664,6 +673,80 @@ "type": "VARCHAR(255)", "default": null, "null": true + }, + "billing_bill_unit": { + "name": "billing_bill_unit", + "type": "INT", + "null": false, + "foreignTable": "unit", + "foreignKey": "unit_id" + } + } + }, + "billing_subscription": { + "description": "https://learn.microsoft.com/en-us/graph/outlook-schedule-recurring-events", + "name": "billing_subscription", + "fields": { + "billing_subscription_id": { + "name": "billing_subscription_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "billing_subscription_status": { + "name": "billing_subscription_status", + "type": "TINYINT(1)", + "null": false + }, + "billing_subscription_start": { + "name": "billing_subscription_start", + "type": "DATETIME", + "null": false + }, + "billing_subscription_end": { + "name": "billing_subscription_end", + "type": "DATETIME", + "null": true, + "default": null + }, + "billing_subscription_price": { + "name": "billing_subscription_price", + "type": "BIGINT", + "null": false + }, + "billing_subscription_quantity": { + "name": "billing_subscription_quantity", + "type": "BIGINT", + "null": false + }, + "billing_subscription_bill": { + "name": "billing_subscription_bill", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "billing_bill", + "foreignKey": "billing_bill_id" + }, + "billing_subscription_item": { + "name": "billing_subscription_item", + "type": "INT", + "null": false, + "foreignTable": "itemmgmt_item", + "foreignKey": "itemmgmt_item_id" + }, + "billing_subscription_autorenew": { + "name": "billing_subscription_autorenew", + "type": "TINYINT(1)", + "null": false + }, + "billing_subscription_client": { + "name": "billing_subscription_client", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "clientmgmt_client", + "foreignKey": "clientmgmt_client_id" } } }, @@ -686,7 +769,17 @@ "name": "billing_bill_element_item", "type": "INT", "null": true, - "default": null + "default": null, + "foreignTable": "itemmgmt_item", + "foreignKey": "itemmgmt_item_id" + }, + "billing_bill_element_subscription": { + "name": "billing_bill_element_subscription", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "billing_subscription", + "foreignKey": "billing_subscription_id" }, "billing_bill_element_item_segment": { "name": "billing_bill_element_item_segment", @@ -1184,46 +1277,6 @@ } } }, - "billing_bill_subscription": { - "description": "https://learn.microsoft.com/en-us/graph/outlook-schedule-recurring-events", - "name": "billing_bill_subscription", - "fields": { - "billing_bill_subscription_id": { - "name": "billing_bill_subscription_id", - "type": "INT", - "null": false, - "primary": true, - "autoincrement": true - }, - "billing_bill_subscription_status": { - "name": "billing_bill_subscription_status", - "type": "TINYINT(1)", - "null": false - }, - "billing_bill_subscription_bill": { - "name": "billing_bill_subscription_bill", - "type": "INT", - "null": false, - "foreignTable": "billing_bill", - "foreignKey": "billing_bill_id" - }, - "billing_bill_subscription_schedule": { - "name": "billing_bill_subscription_schedule", - "type": "INT", - "null": false, - "foreignTable": "schedule", - "foreignKey": "schedule_id" - }, - "billing_bill_subscription_account": { - "name": "billing_bill_subscription_account", - "type": "INT", - "null": true, - "default": null, - "foreignTable": "account", - "foreignKey": "account_id" - } - } - }, "billing_bill_responsible": { "name": "billing_bill_responsible", "fields": { diff --git a/Admin/Install/db.mysql.sql b/Admin/Install/db.mysql.sql new file mode 100644 index 0000000..3b9e5a1 --- /dev/null +++ b/Admin/Install/db.mysql.sql @@ -0,0 +1,11 @@ +CREATE TRIGGER update_billing_bill_sequence +BEFORE INSERT ON billing_bill +FOR EACH ROW BEGIN + SET NEW.billing_bill_sequence = ( + SELECT COALESCE(MAX(billing_bill_sequence), 0) + 1 + FROM billing_bill + WHERE billing_bill_unit = NEW.billing_bill_unit + AND billing_bill_type = NEW.billing_bill_type + LIMIT 1 + ); +END; \ No newline at end of file diff --git a/Admin/Install/db.psql.sql b/Admin/Install/db.psql.sql new file mode 100644 index 0000000..7e3f329 --- /dev/null +++ b/Admin/Install/db.psql.sql @@ -0,0 +1,16 @@ +CREATE SEQUENCE billing_bill_sequence; + +CREATE OR REPLACE FUNCTION update_billing_bill_sequence() + RETURNS TRIGGER AS + $$ + BEGIN + NEW.billing_bill_sequence = nextval('billing_bill_sequence') WHERE billing_bill_unit = NEW.billing_bill_unit; + RETURN NEW; + END; + $$ +LANGUAGE plpgsql; + +CREATE TRIGGER update_sequence_trigger + BEFORE INSERT ON billing_bill + FOR EACH ROW + EXECUTE FUNCTION update_billing_bill_sequence(); diff --git a/Admin/Installer.php b/Admin/Installer.php index 0ab6dde..c830b2a 100755 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -17,6 +17,7 @@ namespace Modules\Billing\Admin; use Modules\Attribute\Models\AttributeTypeMapper; use Modules\Billing\Models\BillTransferType; use Modules\ClientManagement\Models\ClientAttributeTypeMapper; +use Modules\ItemManagement\Models\ItemAttributeTypeMapper; use Modules\SupplierManagement\Models\SupplierAttributeTypeMapper; use phpOMS\Application\ApplicationAbstract; use phpOMS\Config\SettingsInterface; @@ -258,8 +259,8 @@ final class Installer extends InstallerAbstract /** @var \Modules\Billing\Controller\ApiController $module */ $module = $app->moduleManager->getModuleInstance('Billing'); - /** @var \Modules\Attribute\Models\AttributeType $AttributeSales */ - $AttributeSales = AttributeTypeMapper::get() + /** @var \Modules\Attribute\Models\ItemAttributeTypeMapper $itemAttributeSales */ + $itemAttributeSales = ItemAttributeTypeMapper::get() ->with('defaults') ->where('name', 'sales_tax_code') ->execute(); @@ -277,7 +278,7 @@ final class Installer extends InstallerAbstract ->execute(); foreach ($taxes as $tax) { - $itemValue = $AttributeSales->getDefaultByValue($tax['item_code']); + $itemValue = $itemAttributeSales->getDefaultByValue($tax['item_code']); $accountValue = $tax['type'] === 1 ? $clientAttributeSales->getDefaultByValue($tax['account_code']) : $supplierAttributeSales->getDefaultByValue($tax['account_code']); diff --git a/Controller/ApiBillController.php b/Controller/ApiBillController.php index 3ea2e0f..8c16a04 100755 --- a/Controller/ApiBillController.php +++ b/Controller/ApiBillController.php @@ -168,6 +168,15 @@ final class ApiBillController extends Controller { $this->createModel($request->header->account, $bill, BillMapper::class, 'bill', $request->getOrigin()); + // We ned to get the bill again since the bill has a trigger which is executed on insert + // @todo: consider to remove the trigger and select the latest bill here and add + 1 to the new sequence since we have to tdo an update anyways + /** @var Bill $bill */ + $tmp = BillMapper::get() + ->where('id', $bill->getId()) + ->execute(); + + $bill->sequence = $tmp->sequence; + $old = clone $bill; $bill->buildNumber(); // The bill id is part of the number $this->updateModel($request->header->account, $old, $bill, BillMapper::class, 'bill', $request->getOrigin()); @@ -194,6 +203,7 @@ final class ApiBillController extends Controller // @todo: validate vat before creation $bill = new Bill(); $bill->createdBy = new NullAccount($request->header->account); + $bill->unit = $client->unit ?? $this->app->unitId; $bill->billDate = new \DateTime('now'); // @todo: Date of payment $bill->performanceDate = new \DateTime('now'); // @todo: Date of payment $bill->accountNumber = $client->number; @@ -279,8 +289,12 @@ final class ApiBillController extends Controller { $taxCode = $this->app->moduleManager->get('Billing', 'ApiTax')->getTaxCodeFromClientItem($client, $item, $request->getCountry()); - $element = BillElement::fromItem($item, $taxCode, $request->getDataInt('quantity') ?? 1); - $element->bill = $request->getDataInt('bill') ?? 0; + $element = BillElement::fromItem( + $item, + $taxCode, + $request->getDataInt('quantity') ?? 1, + $bill->getId() + ); return $element; } @@ -327,6 +341,7 @@ final class ApiBillController extends Controller // @todo: use defaultInvoiceAddress or mainAddress. also consider to use billto1, billto2, billto3 (for multiple lines e.g. name2, fao etc.) /** @var \Modules\SupplierManagement\Models\Supplier|\Modules\ClientManagement\Models\Client $account */ $bill = new Bill(); + $bill->unit = $account->unit ?? $this->app->unitId; $bill->createdBy = new NullAccount($request->header->account); $bill->type = $billType; $bill->billTo = $request->getDataString('billto') ?? ( diff --git a/Controller/ApiPurchaseController.php b/Controller/ApiPurchaseController.php index 20109d6..1c70c98 100755 --- a/Controller/ApiPurchaseController.php +++ b/Controller/ApiPurchaseController.php @@ -54,10 +54,10 @@ final class ApiPurchaseController extends Controller */ public function apiSupplierBillUpload(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void { - $originalType = $request->getDataInt('type') ?? (int) $this->app->appSettings->get( + $originalType = $request->getDataInt('type') ?? ((int) $this->app->appSettings->get( names: SettingsEnum::ORIGINAL_MEDIA_TYPE, module: self::NAME - )->content; + )->content); /** @var \Modules\Billing\Models\BillType $purchaseTransferType */ $purchaseTransferType = BillTypeMapper::get() diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 1121c3d..9c09a0c 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -128,11 +128,11 @@ final class BackendController extends Controller /** @var \Modules\Auditor\Models\Auditor[] $logsBill */ $logsBill = AuditMapper::getAll() - ->with('createdBy') - ->where('module', 'Billing') - ->where('type', StringUtils::intHash(BillMapper::class)) - ->where('ref', $bill->getId()) - ->execute(); + ->with('createdBy') + ->where('module', 'Billing') + ->where('type', StringUtils::intHash(BillMapper::class)) + ->where('ref', $bill->getId()) + ->execute(); /** @var \Modules\Auditor\Models\Auditor[] $logsElements */ $logsElements = AuditMapper::getAll() diff --git a/Models/Bill.php b/Models/Bill.php index daff05d..d88e80b 100755 --- a/Models/Bill.php +++ b/Models/Bill.php @@ -45,6 +45,21 @@ class Bill implements \JsonSerializable */ protected int $id = 0; + /** + * Sequence. + * + * Incrementing value depending on multiple columns e.g.: + * id & unit + * id & unit & type + * id & unit & year + * + * @var int + * @since 1.0.0 + */ + public int $sequence = 0; + + public int $unit = 0; + public int $source = 0; /** @@ -497,6 +512,7 @@ class Bill implements \JsonSerializable '{m}', '{d}', '{id}', + '{sequence}', '{type}', ], [ @@ -504,6 +520,7 @@ class Bill implements \JsonSerializable $this->createdAt->format('m'), $this->createdAt->format('d'), $this->id, + $this->sequence, $this->type->getId(), ], $this->type->numberFormat diff --git a/Models/BillElement.php b/Models/BillElement.php index 792db58..c66d85c 100755 --- a/Models/BillElement.php +++ b/Models/BillElement.php @@ -50,6 +50,8 @@ class BillElement implements \JsonSerializable protected int $quantity = 0; + public ?Subscription $subscription = null; + public Money $singleSalesPriceNet; public Money $singleSalesPriceGross; @@ -227,15 +229,17 @@ class BillElement implements \JsonSerializable * @param Item $item Item * @param TaxCode $code Tax code used for gross amount calculation * @param int $quantity Quantity + * @param int $bill Bill * * @return self * * @since 1.0.0 */ - public static function fromItem(Item $item, TaxCode $code, int $quantity = 1) : self + public static function fromItem(Item $item, TaxCode $code, int $quantity = 1, int $bill = 0) : self { $element = new self(); - $element->item = $item->getId(); + $element->bill = $bill; + $element->item = empty($item->getId()) ? null : $item->getId(); $element->itemNumber = $item->number; $element->itemName = $item->getL11n('name1')->description; $element->itemDescription = $item->getL11n('description_short')->description; @@ -267,6 +271,20 @@ class BillElement implements \JsonSerializable $element->singleProfitGross->setInt($element->singleSalesPriceGross->getInt() - $element->singlePurchasePriceGross->getInt()); $element->totalProfitGross->setInt($element->quantity * ($element->totalSalesPriceGross->getInt() - $element->totalPurchasePriceGross->getInt())); + if (!empty($element->bill) + && $item->getAttribute('subscription')?->value->getValue() === 1 + ) { + $element->subscription = new Subscription(); + $element->subscription->bill = $element->bill; + $element->subscription->item = $element->item; + $element->subscription->start = $element->quantity; + $element->subscription->end = $element->quantity; + $element->subscription->quantity = $element->quantity; + $element->subscription->autoRenew = $item->getAttribute('subscription_renewal_type')->value->getValue() === 1 + ? true + : false; + } + return $element; } diff --git a/Models/BillElementMapper.php b/Models/BillElementMapper.php index 0e8a691..006c372 100755 --- a/Models/BillElementMapper.php +++ b/Models/BillElementMapper.php @@ -83,6 +83,19 @@ final class BillElementMapper extends DataMapperFactory ], ]; + /** + * Has one relation. + * + * @var array + * @since 1.0.0 + */ + public const OWNS_ONE = [ + 'subscription' => [ + 'mapper' => SubscriptionMapper::class, + 'external' => 'billing_bill_element_subscription', + ], + ]; + /** * Primary field name. * diff --git a/Models/BillMapper.php b/Models/BillMapper.php index 4bdc3c7..c03a2dc 100755 --- a/Models/BillMapper.php +++ b/Models/BillMapper.php @@ -44,6 +44,7 @@ class BillMapper extends DataMapperFactory */ 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'], @@ -90,6 +91,7 @@ class BillMapper extends DataMapperFactory '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'], ]; /** diff --git a/Models/NullSubscription.php b/Models/NullSubscription.php new file mode 100644 index 0000000..80136ce --- /dev/null +++ b/Models/NullSubscription.php @@ -0,0 +1,47 @@ +id = $id; + parent::__construct(); + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return ['id' => $this->id]; + } +} diff --git a/Models/Price/PriceMapper.php b/Models/Price/PriceMapper.php index bda26bc..907b860 100755 --- a/Models/Price/PriceMapper.php +++ b/Models/Price/PriceMapper.php @@ -14,9 +14,9 @@ declare(strict_types=1); namespace Modules\Billing\Models\Price; -use Modules\Attribute\Models\AttributeValueMapper; use Modules\ClientManagement\Models\ClientAttributeValueMapper; use Modules\ClientManagement\Models\ClientMapper; +use Modules\ItemManagement\Models\ItemAttributeValueMapper; use Modules\ItemManagement\Models\ItemMapper; use Modules\SupplierManagement\Models\SupplierMapper; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; @@ -83,19 +83,19 @@ final class PriceMapper extends DataMapperFactory 'external' => 'billing_price_item', ], 'itemgroup' => [ - 'mapper' => AttributeValueMapper::class, + 'mapper' => ItemAttributeValueMapper::class, 'external' => 'billing_price_itemgroup', ], 'itemsegment' => [ - 'mapper' => AttributeValueMapper::class, + 'mapper' => ItemAttributeValueMapper::class, 'external' => 'billing_price_itemsegment', ], 'itemsection' => [ - 'mapper' => AttributeValueMapper::class, + 'mapper' => ItemAttributeValueMapper::class, 'external' => 'billing_price_itemsection', ], 'itemtype' => [ - 'mapper' => AttributeValueMapper::class, + 'mapper' => ItemAttributeValueMapper::class, 'external' => 'billing_price_itemtype', ], 'client' => [ diff --git a/Models/SalesBillMapper.php b/Models/SalesBillMapper.php index 3dc464e..d94a438 100755 --- a/Models/SalesBillMapper.php +++ b/Models/SalesBillMapper.php @@ -229,22 +229,34 @@ final class SalesBillMapper extends BillMapper */ public static function getItemTopClients(int $id, \DateTime $start, \DateTime $end, int $limit = 10) : array { - $query = ClientMapper::getQuery(); - $query->selectAs('SUM(billing_bill_element_total_netsalesprice)', 'net_sales') - ->leftJoin(self::TABLE, self::TABLE . '_d1') - ->on(ClientMapper::TABLE . '_d1.clientmgmt_client_id', '=', self::TABLE . '_d1.billing_bill_client') - ->leftJoin(BillElementMapper::TABLE, BillElementMapper::TABLE . '_d1') - ->on(self::TABLE . '_d1.billing_bill_id', '=', BillElementMapper::TABLE . '_d1.billing_bill_element_bill') - ->where(BillElementMapper::TABLE . '_d1.billing_bill_element_item', '=', $id) - ->andWhere(self::TABLE . '_d1.billing_bill_performance_date', '>=', $start) - ->andWhere(self::TABLE . '_d1.billing_bill_performance_date', '<=', $end) + $query = new Builder(self::$db); + $query->selectAs(ClientMapper::TABLE . '.clientmgmt_client_id', 'client') + ->selectAs('SUM(' . BillElementMapper::TABLE . '.billing_bill_element_total_netsalesprice)', 'net_sales') + ->from(ClientMapper::TABLE) + ->leftJoin( self::TABLE) + ->on(ClientMapper::TABLE . '.clientmgmt_client_id', '=', self::TABLE . '.billing_bill_client') + ->leftJoin(BillElementMapper::TABLE) + ->on(self::TABLE . '.billing_bill_id', '=', BillElementMapper::TABLE . '.billing_bill_element_bill') + ->where(BillElementMapper::TABLE . '.billing_bill_element_item', '=', $id) + ->andWhere(self::TABLE . '.billing_bill_performance_date', '>=', $start) + ->andWhere(self::TABLE . '.billing_bill_performance_date', '<=', $end) ->orderBy('net_sales', 'DESC') ->limit($limit) - ->groupBy(ClientMapper::TABLE . '_d1.clientmgmt_client_id'); + ->groupBy('client'); - /** @var \Modules\ClientManagement\Models\Client[] $clients */ - $clients = ClientMapper::getAll()->execute($query); - $data = ClientMapper::getRaw()->execute(); + $stmt = $query->execute(); + $data = $stmt->fetchAll(); + + $clientIds = []; + foreach ($data as $client) { + $clientIds[] = $client['client']; + } + + if (!empty($clientIds)) { + $clients = ClientMapper::getAll() + ->where('id', $clientIds, 'IN') + ->execute(); + } return [$clients, $data]; } diff --git a/Models/Subscription.php b/Models/Subscription.php new file mode 100644 index 0000000..a07897b --- /dev/null +++ b/Models/Subscription.php @@ -0,0 +1,94 @@ +id; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return [ + 'id' => $this->id, + ]; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return $this->toArray(); + } +} diff --git a/Models/SubscriptionMapper.php b/Models/SubscriptionMapper.php new file mode 100644 index 0000000..0ae85e6 --- /dev/null +++ b/Models/SubscriptionMapper.php @@ -0,0 +1,66 @@ + + */ +final class SubscriptionMapper extends DataMapperFactory +{ + /** + * Columns. + * + * @var array + * @since 1.0.0 + */ + public const COLUMNS = [ + 'billing_subscription_id' => ['name' => 'billing_subscription_id', 'type' => 'int', 'internal' => 'id'], + 'billing_subscription_status' => ['name' => 'billing_subscription_status', 'type' => 'int', 'internal' => 'status'], + '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_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'], + 'billing_subscription_client' => ['name' => 'billing_subscription_client', 'type' => 'int', 'internal' => 'client'], + ]; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD = 'billing_subscription_id'; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'billing_subscription'; +} diff --git a/Models/Tax/TaxCombinationMapper.php b/Models/Tax/TaxCombinationMapper.php index 8494057..1ce12cd 100755 --- a/Models/Tax/TaxCombinationMapper.php +++ b/Models/Tax/TaxCombinationMapper.php @@ -14,8 +14,8 @@ declare(strict_types=1); namespace Modules\Billing\Models\Tax; -use Modules\Attribute\Models\AttributeValueMapper; use Modules\ClientManagement\Models\ClientAttributeValueMapper; +use Modules\ItemManagement\Models\ItemAttributeValueMapper; use Modules\SupplierManagement\Models\SupplierAttributeValueMapper; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; @@ -70,7 +70,7 @@ final class TaxCombinationMapper extends DataMapperFactory 'external' => 'billing_tax_supplier_code', ], 'itemCode' => [ - 'mapper' => AttributeValueMapper::class, + 'mapper' => ItemAttributeValueMapper::class, 'external' => 'billing_tax_item_code', ], ]; diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index 3d3d4a8..742f38c 100755 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -40,6 +40,7 @@ return ['Billing' => [ 'Created' => 'Created', 'CreditCard' => 'CreditCard', 'CreditNote' => 'Credit Note', + 'CreateBill' => 'Create Bill', 'Customers' => 'Customers', 'Date' => 'Date', 'Delivery' => 'Delivery', diff --git a/info.json b/info.json index 84142ce..7d66b30 100755 --- a/info.json +++ b/info.json @@ -20,6 +20,7 @@ "Admin": "1.0.0", "Sales": "1.0.0", "Media": "1.0.0", + "Finance": "1.0.0", "Calendar": "1.0.0", "ItemManagement": "1.0.0", "ClientManagement": "1.0.0",