netProfit = new FloatInt(0); $this->netCosts = new FloatInt(0); $this->netSales = new FloatInt(0); $this->grossSales = new FloatInt(0); $this->netDiscount = new FloatInt(0); $this->taxP = new FloatInt(0); $this->billDate = new \DateTime('now'); $this->createdAt = new \DateTimeImmutable(); $this->createdBy = new NullAccount(); $this->referral = new NullAccount(); $this->type = new NullBillType(); } /** * Build the invoice number. * * @return void * * @since 1.0.0 */ public function buildNumber() : void { $this->number = \str_replace( [ '{y}', '{m}', '{d}', '{id}', '{sequence}', '{type}', '{unit}', '{account}', '{country}', ], [ $this->createdAt->format('Y'), $this->createdAt->format('m'), $this->createdAt->format('d'), $this->id, $this->sequence, $this->type->id, $this->unit, $this->accountNumber, $this->billCountry, ], $this->type->numberFormat ); } /** * Get Bill number. * * @return string * * @since 1.0.0 */ public function getNumber() : string { if (empty($this->number)) { $this->buildNumber(); } return $this->number; } /** * Get paymentStatus * * @return int * * @since 1.0.0 */ public function getPaymentStatus() : int { return $this->paymentStatus; } /** * Set paymentStatus * * @param int $paymentStatus Status * * @return void * * @since 1.0.0 */ public function setPaymentStatus(int $paymentStatus) : void { $this->paymentStatus = $paymentStatus; } /** * Add Bill element. * * @param BillElement $element Bill element * * @return void * * @since 1.0.0 */ public function addElement(BillElement $element) : void { $this->elements[] = $element; $this->netProfit->value += $element->totalProfitNet->value; $this->netCosts->value += $element->totalPurchasePriceNet->value; $this->netSales->value += $element->totalSalesPriceNet->value; $this->grossSales->value += $element->totalSalesPriceGross->value; $this->netDiscount->value += $element->totalDiscountP->value; } /** * Validate the correctness of the bill * * @return bool * * @todo also consider rounding similarly to recalculatePrices in elements * * @since 1.0.0 */ public function isValid() : bool { return $this->validateTaxAmountElements() && $this->validateProfit() && $this->validateGrossElements() && $this->validatePriceQuantityElements() && $this->validateNetElements() && $this->validateNetGross() && $this->areElementsValid(); } /** * Validate the correctness of the bill elements * * @return bool * * @since 1.0.0 */ public function areElementsValid() : bool { foreach ($this->elements as $element) { if (!$element->isValid()) { return false; } } return true; } /** * Validate the correctness of the net and gross values * * @return bool * * @since 1.0.0 */ public function validateNetGross() : bool { return $this->netSales->value <= $this->grossSales->value; } /** * Validate the correctness of the profit * * @return bool * * @since 1.0.0 */ public function validateProfit() : bool { return $this->netSales->value - $this->netCosts->value === $this->netProfit->value; } /** * Validate the correctness of the taxes * * @return bool * * @since 1.0.0 */ public function validateTax() : bool { return \abs($this->netSales->value + $this->taxP->value - $this->grossSales->value) === 0; } /** * Validate the correctness of the taxes for the elements * * @return bool * * @since 1.0.0 */ public function validateTaxAmountElements() : bool { $taxes = 0; foreach ($this->elements as $element) { $taxes += $element->taxP->value; } return $taxes === $this->taxP->value; } /** * Validate the correctness of the net of the elements * * @return bool * * @since 1.0.0 */ public function validateNetElements() : bool { $net = 0; foreach ($this->elements as $element) { $net += $element->totalSalesPriceNet->value; } return $net === $this->netSales->value; } /** * Validate the correctness of the gross of the elements * * @return bool * * @since 1.0.0 */ public function validateGrossElements() { $gross = 0; foreach ($this->elements as $element) { $gross += $element->totalSalesPriceGross->value; } return $gross === $this->grossSales->value; } /** * Validate the correctness of the quantities and total price * * @return bool * * @since 1.0.0 */ public function validatePriceQuantityElements() { foreach ($this->elements as $element) { if ($element->discountQ->value === 0 && $element->totalDiscountP->value === 0 && ($element->quantity->value / FloatInt::DIVISOR) * $element->singleSalesPriceNet->value - $element->totalSalesPriceNet->value > 1.0 ) { return false; } } return true; } /** * {@inheritdoc} */ public function toArray() : array { return [ 'id' => $this->id, 'number' => $this->number, 'external' => $this->external, '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, ]; } /** * {@inheritdoc} */ public function jsonSerialize() : mixed { return $this->toArray(); } use \Modules\Attribute\Models\AttributeHolderTrait; use \Modules\Editor\Models\EditorDocListTrait; use \Modules\Media\Models\MediaListTrait; }