db = $db; } /** * Generate invoice with basic data */ public function createInvoice(array $invoice, $sendId, $company_invoice = []): Facture { global $user; ApiBbusLog::appLog("{$sendId} - createInvoice_________START - " . microtime(true)); $invoice['multicurrency_tx'] = $invoice['multicurrency_code'] === 'HUF' ? 1.00000000 : $invoice['multicurrency_tx']; $invoice['array_options_sales_group'] = $this->getGroupId(); $this->apiInvoice = new ApiInvoice; $this->apiInvoice->loadData($invoice); $this->apiInvoice->prepareData(); $this->apiInvoice->validate(); ApiBbusLog::appLog("{$sendId} - createInvoice_________END - " . microtime(true)); return $this->apiInvoice->create($company_invoice); } function getGroupId() { global $user; $sql = "SELECT fk_settlements_group FROM llx_settlements_groupusers WHERE fk_user = {$user->id}"; $result = $this->db->query($sql); if ($this->db->num_rows($result) > 0) { while ($row = $this->db->fetch_object($result)) { return $row->fk_settlements_group; } } return null; } function increaseParticipant($fk_event) { global $user; $actionComObj = new ActionComm($this->db); $actionComObj->fetch($fk_event); $participants = (int) $actionComObj->array_options['options_participants']; $increasedParticipants = $participants + 1; $actionComObj->array_options['options_participants'] = (int) $increasedParticipants; $actionComObj->label = $fk_event; $actionComObj->update($user); } /** * GEnerate PROV invoice */ public function createInvoicePROV(array $invoice, $sendId): Facture { ApiBbusLog::eventLog("{$sendId} - createInvoice_________START - " . microtime(true)); $this->apiInvoice = new ApiInvoice; $invoice['multicurrency_tx'] = $invoice['multicurrency_code'] === 'HUF' ? 1.00000000 : $invoice['multicurrency_tx']; $invoice['array_options_sales_group'] = $this->getGroupId(); //print_r($invoice);exit; $this->apiInvoice->loadData($invoice); $this->apiInvoice->prepareData(); $this->apiInvoice->validate(); return $this->apiInvoice->create(); } /** * Add product to invoice */ public function addLineToInvoice(Facture $invoice, array $line, $sendId): Facture { ApiBbusLog::appLog("{$sendId} addLineToInvoice: START"); $tva_tx_array = explode('.', $line['tva_tx']); $line['tva_tx'] = $line['tva_tx'] . '000 (' . $tva_tx_array[0] . ')'; //ApiBbusLog::appLog("{$sendId} - createInvoiceLINE_________START - " . microtime(true)); $apiInvoiceLine = new ApiInvoiceLine; $apiInvoiceLine->setInvoice($invoice); $apiInvoiceLine->loadData($line); $apiInvoiceLine->prepareData(); $invoice = $apiInvoiceLine->create(); return $invoice; } function getProductLines($product_id) { $productObj = new Product($this->db); $result = $productObj->fetch($product_id); $productObj->fk_product = $product_id; $productObj->subprice = $productObj->price_ttc; $productObj->qty = 1; $lines[] = (array) $productObj; return $lines; } function getProductLinesWithoutDiscount($product_id) { ApiBbusLog::appLog("getProductLinesWithoutDiscount: {$product_id}"); $productObj = new Product($this->db); $result = $productObj->fetch($product_id); $productObj->fk_product = $product_id; //$productObj->subprice = $productObj->price_ttc; $productObj->subprice = $productObj->price; $productObj->qty = 1; $productObj->array_options['options_discount_hours'] = null; $productObj->array_options['options_discount_percent'] = null; $lines[] = (array) $productObj; return $lines; } /** * Validate invoice */ public function validateInvoice(Facture $invoice, $sendId): Facture { ApiBbusLog::appLog("{$sendId} - validateInvoice_________START - " . microtime(true)); return $this->apiInvoice->validateInvoice($invoice, 1); } /** * Validate invoice */ public function validateInvoiceFromPROV(Facture $invoice, $sendId, $facture_id): Facture { $this->apiInvoice = new ApiInvoice; ApiBbusLog::eventLog("{$sendId} - validateInvoice_________START - " . microtime(true)); return $this->apiInvoice->validateInvoice($invoice, 1); } /** * Set payment * * @return int Payment ID */ public function setPaymentFromPROV(Facture $invoice, array $payment, string $sendId): int { $this->apiInvoice = new ApiInvoice; ApiBbusLog::eventLog("{$sendId} - setPayment_________START - " . microtime(true)); return $this->apiInvoice->setPayment($invoice, $payment, $sendId); } /** * Set payment * * @return int Payment ID */ public function setPayment(Facture $invoice, array $payment, string $sendId): int { ApiBbusLog::appLog("{$sendId} - setPayment_________START - " . microtime(true)); return $this->apiInvoice->setPayment($invoice, $payment, $sendId); } /** * Save card payment log to invoice */ public function saveCardPaymentLog(Facture $invoice, string $cardPaymentLog, $sendId): Facture { //ApiBbusLog::appLog("{$sendId} - saveCardPaymentLog_________START - " . microtime(true)); $invoice->array_options['options_payment_log'] = $cardPaymentLog; $invoice->updateExtraField('payment_log'); $json = json_decode($cardPaymentLog, true); if (!empty($json['authorization_number'])) { $sql = " UPDATE " . MAIN_DB_PREFIX . "paiement SET num_paiement='" . $json['authorization_number'] . "' WHERE rowid=( SELECT fk_paiement FROM " . MAIN_DB_PREFIX . "paiement_facture WHERE fk_facture={$invoice->id} ) "; $this->db->query($sql); } return $invoice; } /** * Load product */ public function loadProductToResult(array $line): array { $result = []; $product = new Product($this->db); if ($product->fetch($line['fk_product']) > 0) { $result = [ 'id' => $product->id, 'ref' => $product->ref, 'label' => $product->label, 'description' => $product->description, 'vat' => $product->tva_tx, 'discount_period' => (empty($line['array_options']['discount_period'])) ? '' : $line['array_options']['discount_period'], 'discount_value' => (empty($line['array_options']['discount_value'])) ? '' : $line['array_options']['discount_value'], 'orig_price' => $product->price_ttc, 'price' => $product->price_ttc ]; } return $result; } } /** * * */ interface ApiInvoiceData { public function loadData(array $data): void; public function prepareData(): void; public function validate(): void; public function create(); // missing return type because of the different result objects } /** * * */ class ApiInvoiceLine implements ApiInvoiceData { public $data; public $invoice; /** * */ public function loadData(array $data): void { $this->data = (object) $data; } /** * */ public function setInvoice(Facture $invoice): void { $this->invoice = $invoice; } /** * */ public function prepareData(): void { $this->data->desc = sanitizeVal($this->data->desc, 'restricthtml'); $this->data->label = sanitizeVal($this->data->label); if (($this->data->product_type != 9 && empty($this->data->fk_parent_line)) || $this->data->product_type == 9) { $this->data->fk_parent_line = 0; } } /** * */ public function validate(): void { // } /** * */ public function create(): Facture { $marginInfos = getMarginInfos($this->data->subprice, $this->data->remise_percent, $this->data->tva_tx, $this->data->localtax1_tx, $this->data->localtax2_tx, $this->data->fk_fournprice, $this->data->pa_ht); $pa_ht = $marginInfos[0]; $updateRes = $this->invoice->addline( $this->data->desc, $this->data->subprice, $this->data->qty, $this->data->tva_tx, $this->data->localtax1_tx, $this->data->localtax2_tx, $this->data->fk_product, $this->data->remise_percent, $this->data->date_start, $this->data->date_end, $this->data->fk_code_ventilation, $this->data->info_bits, $this->data->fk_remise_except, $this->data->price_base_type ? $this->data->price_base_type : 'HT', $this->data->subprice, $this->data->product_type, $this->data->rang, $this->data->special_code, $this->data->origin, $this->data->origin_id, $this->data->fk_parent_line, empty($this->data->fk_fournprice) ? null : $this->data->fk_fournprice, $pa_ht, $this->data->label, $this->data->array_options, $this->data->situation_percent, $this->data->fk_prev_id, $this->data->fk_unit, 0, $this->data->ref_ext ); if ($updateRes < 0) { throw new RestException(400, 'Unable to insert the new line. Check your inputs. ' . $this->invoice->error); } return $this->invoice; } } /** * * */ class ApiInvoice implements ApiInvoiceData { const SOCID = 1; public $data; public $invoice; /** * */ public function loadData(array $data): void { $this->invoice = null; $this->data = $data; } /** * */ public function prepareData(): void { global $user; $this->data['socid'] = self::SOCID; $this->data['entity'] = $user->entity; } /** * */ public function validate(): void { $fields = [ 'socid', 'entity' ]; foreach ($fields as $field) { if (!isset($this->data[$field])) { throw new RestException(400, "Invoice {$field} field missing"); } } } /** * */ public function create($company_invoice = []): Facture { global $db; $this->invoice = new Facture($db); foreach ($this->data as $field => $value) { if (preg_match('/^array_options/', $field)) { $f = str_replace('array_options_', '', $field); $this->invoice->array_options[$f] = $value; continue; } $this->invoice->$field = $value; } if (!array_key_exists('date', $this->data)) { $this->invoice->date = dol_now(); } if(empty($company_invoice)){ $this->invoice->type = 7; } if ($this->invoice->create(DolibarrApiAccess::$user, 0, (empty($this->data['date_lim_reglement']) ? 0 : $this->data['date_lim_reglement'])) < 0) { throw new RestException(500, 'Error creating invoice', array_merge([$this->invoice->error], $this->invoice->errors)); } return $this->invoice; } /** * */ public function validateInvoice(Facture $invoice, int $notrigger): Facture { $result = $invoice->validate(DolibarrApiAccess::$user, '', 0, $notrigger); if ($result == 0) { throw new RestException(304, 'Error nothing done. May be object is already validated'); } if ($result < 0) { throw new RestException(500, 'Error when validating Invoice: ' . $invoice->error); } return $invoice; } /** * */ public function setPayment(Facture $invoice, array $data, string $sendId): int { ApiBbusLog::appLog("ApiinvoiceHelper: setPayment function"); global $db; if (isModEnabled('banque')) { if (empty($data['accountid'])) { ApiBbusLog::appLog("ApiinvoiceHelper: setPayment accountid error"); throw new RestException(400, 'Account ID is mandatory'); } } if (empty($data['paymentid'])) { ApiBbusLog::appLog("ApiinvoiceHelper: setPayment paymentid error"); throw new RestException(400, 'Payment ID or Payment Code is mandatory'); } // Calculate amount to pay $totalpaid = $invoice->getSommePaiement(); $totalcreditnotes = $invoice->getSumCreditNotesUsed(); $totaldeposits = $invoice->getSumDepositsUsed(); $resteapayer = price2num($invoice->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT'); $db->begin(); $amounts = []; $multicurrency_amounts = []; // Clean parameters amount if payment is for a credit note if ($invoice->type == Facture::TYPE_CREDIT_NOTE) { $resteapayer = price2num($resteapayer, 'MT'); $amounts[$invoice->id] = -$resteapayer; // Multicurrency $newvalue = price2num($invoice->multicurrency_total_ttc, 'MT'); $multicurrency_amounts[$invoice->id] = -$newvalue; } else { $resteapayer = price2num($resteapayer, 'MT'); $amounts[$invoice->id] = $resteapayer; // Multicurrency $newvalue = price2num($invoice->multicurrency_total_ttc, 'MT'); $multicurrency_amounts[$invoice->id] = $newvalue; } // Creation of payment line $paymentobj = new Paiement($db); $paymentobj->datepaye = $data['datepaye']; $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching $paymentobj->paiementid = $data['paymentid']; $paymentobj->paiementcode = dol_getIdFromCode($db, $data['paymentid'], 'c_paiement', 'id', 'code', 1); $paymentobj->num_payment = $data['num_payment']; $paymentobj->note_private = (is_array($data['comment'])) ? json_encode($data['comment']) : $data['comment']; $paymentId = $paymentobj->create(DolibarrApiAccess::$user, ($data['closepaidinvoices'] == 'yes' ? 1 : 0)); // This include closing invoices ApiBbusLog::appLog("paymentId created"); if ($paymentId < 0) { $db->rollback(); ApiBbusLog::appLog("{$sendId} Payment error : " . $paymentobj->error); throw new RestException(400, 'Payment error : ' . $paymentobj->error); } ApiBbusLog::appLog("paymentId created"); if (isModEnabled('banque')) { $label = '(CustomerInvoicePayment)'; $chqemetteur = ''; $chqbank = ''; if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) { ApiBbusLog::appLog("{$sendId} Emetteur is mandatory when payment code is " . $paymentobj->paiementcode); throw new RestException(400, 'Emetteur is mandatory when payment code is ' . $paymentobj->paiementcode); } if ($invoice->type == Facture::TYPE_CREDIT_NOTE) { $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note } $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $data['accountid'], $chqemetteur, $chqbank); if ($result < 0) { $db->rollback(); ApiBbusLog::appLog("{$sendId} Add payment to bank error : " . $paymentobj->error); throw new RestException(400, 'Add payment to bank error : ' . $paymentobj->error); } } # # set Facture -> payment_transaction_id # if (!empty($data['comment'])) { $commentJSON = json_decode($data['comment']); $invoice->array_options['options_payment_transaction_id'] = $commentJSON->card_transaction_id; $invoice->updateExtraField('payment_transaction_id'); } $db->commit(); ApiBbusLog::appLog("{$sendId} PAYMENT CREATED: {$paymentId}"); return $paymentId; } }