diff --git a/.directory b/.directory deleted file mode 100755 index 94bd94d..0000000 --- a/.directory +++ /dev/null @@ -1,6 +0,0 @@ -[Dolphin] -Timestamp=2021,2,7,12,37,21 -Version=4 - -[Settings] -HiddenFilesShown=true diff --git a/Admin/Install/Admin.install.php b/Admin/Install/Admin.install.php new file mode 100644 index 0000000..088fb70 --- /dev/null +++ b/Admin/Install/Admin.install.php @@ -0,0 +1,19 @@ +createBillFromRequest($request, $response, $data); $this->createModel($request->header->account, $bill, BillMapper::class, 'bill', $request->getOrigin()); + + $new = clone $bill; + $new->buildNumber(); // The bill id is part of the number + $this->updateModel($request->header->account, $bill, $new, BillMapper::class, 'bill', $request->getOrigin()); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Bill', 'Bill successfully created.', $bill); } @@ -128,6 +133,73 @@ final class ApiController extends Controller return []; } + public function apiMediaAddToBill(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateMediaAddToBill($request))) { + $response->set($request->uri->__toString(), new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + if (!empty($uploadedFiles = $request->getFiles() ?? [])) { + $uploaded = $this->app->moduleManager->get('Media')->uploadFiles( + [], + [], + $uploadedFiles, + $request->header->account, + __DIR__ . '/../../../Modules/Media/Files/Modules/Editor', + '/Modules/Editor', + ); + + foreach ($uploaded as $media) { + $this->createModelRelation( + $request->header->account, + $request->getData('bill'), + $media->getId(), + BillMapper::class, 'media', '', $request->getOrigin() + ); + } + } + + if (!empty($mediaFiles = $request->getDataJson('media') ?? [])) { + foreach ($mediaFiles as $media) { + $this->createModelRelation( + $request->header->account, + $request->getData('bill'), + $media, + BillMapper::class, 'media', '', $request->getOrigin() + ); + } + } + + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Media', 'Media added to bill.', [ + 'upload' => $uploaded, + 'media' => $mediaFiles + ]); + } + + /** + * Method to validate bill creation from request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateMediaAddToBill(RequestAbstract $request) : array + { + $val = []; + if (($val['media'] = empty($request->getData('media')) && empty($request->getFiles())) + || ($val['bill'] = empty($request->getData('bill'))) + ) { + return $val; + } + + return []; + } + /** * Api method to create a bill element * @@ -338,4 +410,57 @@ final class ApiController extends Controller public function apiBillPdfCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void { } + + /** + * Api method to create item files + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiNoteCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateNoteCreate($request))) { + $response->set('item_note_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $request->setData('virtualpath', '/Modules/Billing/Articles/' . $request->getData('id'), true); + $this->app->moduleManager->get('Editor')->apiEditorCreate($request, $response, $data); + + if ($response->header->status !== RequestStatusCode::R_200) { + return; + } + + $model = $response->get($request->uri->__toString())['response']; + $this->createModelRelation($request->header->account, $request->getData('id'), $model->getId(), BillMapper::class, 'notes', '', $request->getOrigin()); + } + + /** + * Validate item note create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateNoteCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['id'] = empty($request->getData('id'))) + ) { + return $val; + } + + return []; + } } diff --git a/Models/Bill.php b/Models/Bill.php index c231cec..69f39a0 100755 --- a/Models/Bill.php +++ b/Models/Bill.php @@ -21,6 +21,8 @@ use Modules\Media\Models\Media; use Modules\SupplierManagement\Models\Supplier; use phpOMS\Localization\ISO4217CharEnum; use phpOMS\Localization\Money; +use Modules\Editor\Models\EditorDoc; +use Mpdf\Tag\P; /** * Bill class. @@ -48,6 +50,14 @@ class Bill implements \JsonSerializable */ public string $number = ''; + /** + * Number format ID. + * + * @var string + * @since 1.0.0 + */ + public string $numberFormat = ''; + /** * Bill type. * @@ -96,6 +106,14 @@ class Bill implements \JsonSerializable */ public Account $createdBy; + /** + * Files. + * + * @var EditorDoc[] + * @since 1.0.0 + */ + private array $notes = []; + public ?Client $client = null; public ?Supplier $supplier = null; @@ -212,7 +230,7 @@ class Bill implements \JsonSerializable * @var Money * @since 1.0.0 */ - public Money $net; + public Money $netProfit; /** * Gross amount. @@ -220,7 +238,7 @@ class Bill implements \JsonSerializable * @var Money * @since 1.0.0 */ - public Money $gross; + public Money $grossProfit; /** * Costs in net. @@ -228,7 +246,7 @@ class Bill implements \JsonSerializable * @var Money * @since 1.0.0 */ - public Money $costs; + public Money $netCosts; /** * Profit in net. @@ -236,7 +254,39 @@ class Bill implements \JsonSerializable * @var Money * @since 1.0.0 */ - public Money $profit; + public Money $grossCosts; + + /** + * Costs in net. + * + * @var Money + * @since 1.0.0 + */ + public Money $netSales; + + /** + * Profit in net. + * + * @var Money + * @since 1.0.0 + */ + public Money $grossSales; + + /** + * Costs in net. + * + * @var Money + * @since 1.0.0 + */ + public Money $netDiscount; + + /** + * Profit in net. + * + * @var Money + * @since 1.0.0 + */ + public Money $grossDiscount; /** * Insurance fees in net. @@ -365,10 +415,14 @@ class Bill implements \JsonSerializable */ public function __construct() { - $this->net = new Money(0); - $this->gross = new Money(0); - $this->costs = new Money(0); - $this->profit = new Money(0); + $this->netProfit = new Money(0); + $this->grossProfit = new Money(0); + $this->netCosts = new Money(0); + $this->grossCosts = new Money(0); + $this->netSales = new Money(0); + $this->grossSales = new Money(0); + $this->netDiscount = new Money(0); + $this->grossDiscount = new Money(0); $this->createdAt = new \DateTimeImmutable(); $this->performanceDate = new \DateTime(); @@ -389,17 +443,15 @@ class Bill implements \JsonSerializable } /** - * Get Bill number. + * Build the invoice number. * - * @return string + * @return void * * @since 1.0.0 */ - public function getNumber() : string + public function buildNumber() : void { - $number = $this->number; - - return \str_replace( + $this->number = \str_replace( [ '{y}', '{m}', @@ -414,10 +466,22 @@ class Bill implements \JsonSerializable $this->id, \is_int($this->type) ? $this->type : $this->type->getId(), ], - $number + $this->numberFormat ); } + /** + * Get Bill number. + * + * @return string + * + * @since 1.0.0 + */ + public function getNumber() : string + { + return $this->number; + } + /** * Get type * @@ -574,6 +638,32 @@ class Bill implements \JsonSerializable $this->elements[] = $element; } + /** + * Add note to item + * + * @param EditorDoc $note Note + * + * @return void + * + * @since 1.0.0 + */ + public function addNote(EditorDoc $note) : void + { + $this->notes[] = $note; + } + + /** + * Get notes + * + * @return EditorDoc[] + * + * @since 1.0.0 + */ + public function getNotes() : array + { + return $this->notes; + } + /** * Get all media * @@ -629,6 +719,7 @@ class Bill implements \JsonSerializable return [ 'id' => $this->id, 'number' => $this->number, + 'numberFormat' => $this->numberFormat, 'type' => $this->type, 'shipTo' => $this->shipTo, 'shipFAO' => $this->shipFAO, diff --git a/Models/BillElement.php b/Models/BillElement.php index f3f12ce..908d19b 100755 --- a/Models/BillElement.php +++ b/Models/BillElement.php @@ -49,8 +49,12 @@ class BillElement implements \JsonSerializable public Money $singleSalesPriceNet; + public Money $singleSalesPriceGross; + public Money $totalSalesPriceNet; + public Money $totalSalesPriceGross; + public ?FloatInt $singleDiscountP = null; public ?FloatInt $totalDiscountP = null; @@ -59,22 +63,34 @@ class BillElement implements \JsonSerializable public ?FloatInt $discountQ = null; - public ?FloatInt $singlePriceNet = null; + public Money $singleListPriceNet; - public ?FloatInt $totalPriceNet = null; + public Money $singleListPriceGross; + + public Money $totalListPriceNet; + + public Money $totalListPriceGross; public Money $singlePurchasePriceNet; + public Money $singlePurchasePriceGross; + public Money $totalPurchasePriceNet; + public Money $totalPurchasePriceGross; + + public Money $singleProfitNet; + + public Money $singleProfitGross; + + public Money $totalProfitNet; + + public Money $totalProfitGross; + public ?FloatInt $taxP = null; public ?FloatInt $taxR = null; - public ?FloatInt $singleSalesPriceGross = null; - - public ?FloatInt $totalSalesPriceGross = null; - /** * Event assigned to this element. * @@ -100,11 +116,29 @@ class BillElement implements \JsonSerializable */ public function __construct() { + $this->singleListPriceNet = new Money(); + $this->singleListPriceGross = new Money(); + + $this->totalListPriceNet = new Money(); + $this->totalListPriceGross = new Money(); + $this->singleSalesPriceNet = new Money(); + $this->singleSalesPriceGross = new Money(); + $this->totalSalesPriceNet = new Money(); + $this->totalSalesPriceGross = new Money(); $this->singlePurchasePriceNet = new Money(); + $this->singlePurchasePriceGross = new Money(); + $this->totalPurchasePriceNet = new Money(); + $this->totalPurchasePriceGross = new Money(); + + $this->singleProfitNet = new Money(); + $this->singleProfitGross = new Money(); + + $this->totalProfitNet = new Money(); + $this->totalProfitGross = new Money(); } /** diff --git a/Models/BillElementMapper.php b/Models/BillElementMapper.php index 1fe80ee..a2cb12d 100755 --- a/Models/BillElementMapper.php +++ b/Models/BillElementMapper.php @@ -40,10 +40,26 @@ final class BillElementMapper extends DataMapperAbstract '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'], - 'billing_bill_element_single_salesprice_net' => ['name' => 'billing_bill_element_single_salesprice_net', 'type' => 'Serializable', 'internal' => 'singleSalesPriceNet'], - 'billing_bill_element_single_purchaseprice_net' => ['name' => 'billing_bill_element_single_purchaseprice_net', 'type' => 'Serializable', 'internal' => 'singlePurchasePriceNet'], - 'billing_bill_element_total_salesprice_net' => ['name' => 'billing_bill_element_total_salesprice_net', 'type' => 'Serializable', 'internal' => 'totalSalesPriceNet'], - 'billing_bill_element_total_purchaseprice_net' => ['name' => 'billing_bill_element_total_purchaseprice_net', 'type' => 'Serializable', 'internal' => 'totalPurchasePriceNet'], + + '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_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'], + 'billing_bill_element_total_netpurchaseprice' => ['name' => 'billing_bill_element_total_netpurchaseprice', 'type' => 'Serializable', 'internal' => 'totalPurchasePriceNet'], + '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'], ]; diff --git a/Models/BillMapper.php b/Models/BillMapper.php index b27e396..e6bb02e 100755 --- a/Models/BillMapper.php +++ b/Models/BillMapper.php @@ -19,6 +19,7 @@ use Modules\ClientManagement\Models\ClientMapper; use Modules\Media\Models\MediaMapper; use Modules\SupplierManagement\Models\SupplierMapper; use phpOMS\DataStorage\Database\DataMapperAbstract; +use Modules\Editor\Models\EditorDocMapper; /** * Mapper class. @@ -39,6 +40,7 @@ class BillMapper extends DataMapperAbstract protected static array $columns = [ 'billing_bill_id' => ['name' => 'billing_bill_id', 'type' => 'int', 'internal' => 'id'], 'billing_bill_number' => ['name' => 'billing_bill_number', 'type' => 'string', 'internal' => 'number'], + 'billing_bill_numberformat' => ['name' => 'billing_bill_numberformat', 'type' => 'string', 'internal' => 'numberFormat'], 'billing_bill_type' => ['name' => 'billing_bill_type', 'type' => 'int', 'internal' => 'type'], 'billing_bill_info' => ['name' => 'billing_bill_info', 'type' => 'string', 'internal' => 'info'], 'billing_bill_status' => ['name' => 'billing_bill_status', 'type' => 'int', 'internal' => 'status'], @@ -54,10 +56,14 @@ class BillMapper extends DataMapperAbstract '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_gross' => ['name' => 'billing_bill_gross', 'type' => 'Serializable', 'internal' => 'gross'], - 'billing_bill_net' => ['name' => 'billing_bill_net', 'type' => 'Serializable', 'internal' => 'net'], - 'billing_bill_costs' => ['name' => 'billing_bill_costs', 'type' => 'Serializable', 'internal' => 'costs'], - 'billing_bill_profit' => ['name' => 'billing_bill_profit', 'type' => 'Serializable', 'internal' => 'profit'], + '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_referral' => ['name' => 'billing_bill_referral', 'type' => 'int', 'internal' => 'referral'], 'billing_bill_referral_name' => ['name' => 'billing_bill_referral_name', 'type' => 'string', 'internal' => 'referralName'], @@ -94,6 +100,12 @@ class BillMapper extends DataMapperAbstract 'external' => 'billing_bill_media_dst', 'self' => 'billing_bill_media_src', ], + 'notes' => [ + 'mapper' => EditorDocMapper::class, /* mapper of the related object */ + 'table' => 'billing_bill_note', /* table of the related object, null if no relation table is used (many->1) */ + 'external' => 'billing_bill_note_doc', + 'self' => 'billing_bill_note_item', + ], ]; /** diff --git a/Models/SettingsEnum.php b/Models/SettingsEnum.php new file mode 100644 index 0000000..3e95b04 --- /dev/null +++ b/Models/SettingsEnum.php @@ -0,0 +1,30 @@ +