From 5c5bb6ec7cef683f367cbfa6c2552198c5f01769 Mon Sep 17 00:00:00 2001 From: tnegre Date: Fri, 24 Mar 2023 17:10:25 +0100 Subject: [PATCH 1/2] Allow to pay a contract line from a stripe payment. TODO pay the whole contract. --- htdocs/compta/facture/class/facture.class.php | 135 ++++++++++++++++++ htdocs/public/payment/paymentok.php | 124 +++++++++++++++- 2 files changed, 258 insertions(+), 1 deletion(-) diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index b9b4ff3d1c2..478cd326103 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -1482,6 +1482,141 @@ class Facture extends CommonInvoice } } + /** + * Load an object from an order and create a new invoice into database + * + * @param Object $object Object source + * @param User $user Object user + * @param Array(int) $lines Ids of lines to use for invoice. If empty, all lines will be used. + * @return int <0 if KO, 0 if nothing done, 1 if OK + */ + public function createFromContract($object, User $user, $lines = array()) + { + global $conf, $hookmanager; + + $error = 0; + + // Closed order + $this->date = dol_now(); + $this->source = 0; + + $use_all_lines = empty($lines); + $num = count($object->lines); + for ($i = 0; $i < $num; $i++) { + + if (!$use_all_lines && !in_array($object->lines[$i]->id, $lines)) continue; + + $line = new FactureLigne($this->db); + + $line->libelle = $object->lines[$i]->libelle; // deprecated + $line->label = $object->lines[$i]->label; + $line->desc = $object->lines[$i]->desc; + $line->subprice = $object->lines[$i]->subprice; + $line->total_ht = $object->lines[$i]->total_ht; + $line->total_tva = $object->lines[$i]->total_tva; + $line->total_localtax1 = $object->lines[$i]->total_localtax1; + $line->total_localtax2 = $object->lines[$i]->total_localtax2; + $line->total_ttc = $object->lines[$i]->total_ttc; + $line->vat_src_code = $object->lines[$i]->vat_src_code; + $line->tva_tx = $object->lines[$i]->tva_tx; + $line->localtax1_tx = $object->lines[$i]->localtax1_tx; + $line->localtax2_tx = $object->lines[$i]->localtax2_tx; + $line->qty = $object->lines[$i]->qty; + $line->fk_remise_except = $object->lines[$i]->fk_remise_except; + $line->remise_percent = $object->lines[$i]->remise_percent; + $line->fk_product = $object->lines[$i]->fk_product; + $line->info_bits = $object->lines[$i]->info_bits; + $line->product_type = $object->lines[$i]->product_type; + $line->rang = $object->lines[$i]->rang; + $line->special_code = $object->lines[$i]->special_code; + $line->fk_parent_line = $object->lines[$i]->fk_parent_line; + $line->fk_unit = $object->lines[$i]->fk_unit; + $line->date_start = $object->lines[$i]->date_start; + $line->date_end = $object->lines[$i]->date_end; + + // Multicurrency + $line->fk_multicurrency = $object->lines[$i]->fk_multicurrency; + $line->multicurrency_code = $object->lines[$i]->multicurrency_code; + $line->multicurrency_subprice = $object->lines[$i]->multicurrency_subprice; + $line->multicurrency_total_ht = $object->lines[$i]->multicurrency_total_ht; + $line->multicurrency_total_tva = $object->lines[$i]->multicurrency_total_tva; + $line->multicurrency_total_ttc = $object->lines[$i]->multicurrency_total_ttc; + + $line->fk_fournprice = $object->lines[$i]->fk_fournprice; + $marginInfos = getMarginInfos($object->lines[$i]->subprice, $object->lines[$i]->remise_percent, $object->lines[$i]->tva_tx, $object->lines[$i]->localtax1_tx, $object->lines[$i]->localtax2_tx, $object->lines[$i]->fk_fournprice, $object->lines[$i]->pa_ht); + $line->pa_ht = $marginInfos[0]; + + // get extrafields from original line + $object->lines[$i]->fetch_optionals(); + foreach ($object->lines[$i]->array_options as $options_key => $value) { + $line->array_options[$options_key] = $value; + } + + $this->lines[$i] = $line; + } + + $this->socid = $object->socid; + $this->fk_project = $object->fk_project; + $this->fk_account = $object->fk_account; + $this->cond_reglement_id = $object->cond_reglement_id; + $this->mode_reglement_id = $object->mode_reglement_id; + $this->availability_id = $object->availability_id; + $this->demand_reason_id = $object->demand_reason_id; + $this->delivery_date = (empty($object->delivery_date) ? $object->date_livraison : $object->delivery_date); + $this->date_livraison = $object->delivery_date; // deprecated + $this->fk_delivery_address = $object->fk_delivery_address; // deprecated + $this->contact_id = $object->contact_id; + $this->ref_client = $object->ref_client; + + if (empty($conf->global->MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN)) { + $this->note_private = $object->note_private; + $this->note_public = $object->note_public; + } + + $this->module_source = $object->module_source; + $this->pos_source = $object->pos_source; + + $this->origin = $object->element; + $this->origin_id = $object->id; + + $this->fk_user_author = $user->id; + + // get extrafields from original line + $object->fetch_optionals(); + foreach ($object->array_options as $options_key => $value) { + $this->array_options[$options_key] = $value; + } + + // Possibility to add external linked objects with hooks + $this->linked_objects[$this->origin] = $this->origin_id; + if (!empty($object->other_linked_objects) && is_array($object->other_linked_objects)) { + $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects); + } + + $ret = $this->create($user); + + if ($ret > 0) { + // Actions hooked (by external module) + $hookmanager->initHooks(array('invoicedao')); + + $parameters = array('objFrom'=>$object); + $action = ''; + $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + $this->setErrorsFromObject($hookmanager); + $error++; + } + + if (!$error) { + return 1; + } else { + return -1; + } + } else { + return -1; + } + } + /** * Creates a deposit from a proposal or an order by grouping lines by VAT rates * diff --git a/htdocs/public/payment/paymentok.php b/htdocs/public/payment/paymentok.php index 84110d58353..0c731d99ca4 100644 --- a/htdocs/public/payment/paymentok.php +++ b/htdocs/public/payment/paymentok.php @@ -1579,7 +1579,129 @@ if ($ispaymentok) { $postactionmessages[] = 'Invoice paid '.$tmptag['ATT'].' was not found'; $ispostactionok = -1; } - } else { + } elseif (array_key_exists('CON', $tmptag) && array_key_exists('COL', $tmptag) && $tmptag['CON'] > 0 && $tmptag['COL'] > 0) { + include_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php'; + $object = new Contrat($db); + $result = $object->fetch((int) $tmptag['CON']); + if ($result) { + $FinalPaymentAmt = $_SESSION["FinalPaymentAmt"]; + + $paymentTypeId = 0; + if ($paymentmethod == 'paybox') { + $paymentTypeId = $conf->global->PAYBOX_PAYMENT_MODE_FOR_PAYMENTS; + } + if ($paymentmethod == 'paypal') { + $paymentTypeId = $conf->global->PAYPAL_PAYMENT_MODE_FOR_PAYMENTS; + } + if ($paymentmethod == 'stripe') { + $paymentTypeId = $conf->global->STRIPE_PAYMENT_MODE_FOR_PAYMENTS; + } + if (empty($paymentTypeId)) { + dol_syslog("paymentType = ".$paymentType, LOG_DEBUG, 0, '_payment'); + + if (empty($paymentType)) { + $paymentType = 'CB'; + } + // May return nothing when paymentType means nothing + // (for example when paymentType is 'Mark', 'Sole', 'Sale', for paypal) + $paymentTypeId = dol_getIdFromCode($db, $paymentType, 'c_paiement', 'code', 'id', 1); + + // If previous line has returned nothing, we force to get the ID of payment of Credit Card (hard coded code 'CB'). + if (empty($paymentTypeId) || $paymentTypeId < 0) { + $paymentTypeId = dol_getIdFromCode($db, 'CB', 'c_paiement', 'code', 'id', 1); + } + } + + $currencyCodeType = $_SESSION['currencyCodeType']; + + // Do action only if $FinalPaymentAmt is set (session variable is cleaned after this page to avoid duplicate actions when page is POST a second time) + if (isModEnabled('facture')) { + if (!empty($FinalPaymentAmt) && $paymentTypeId > 0 ) { + include_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php'; + $invoice = new Facture($db); + $result = $invoice->createFromContract($object, $user, array((int) $tmptag['COL'])); + if ($result > 0) { + // $object->classifyBilled($user); + $invoice->validate($user); + // Creation of payment line + include_once DOL_DOCUMENT_ROOT . '/compta/paiement/class/paiement.class.php'; + $paiement = new Paiement($db); + $paiement->datepaye = $now; + if ($currencyCodeType == $conf->currency) { + $paiement->amounts = array($invoice->id => $FinalPaymentAmt); // Array with all payments dispatching with invoice id + } else { + $paiement->multicurrency_amounts = array($invoice->id => $FinalPaymentAmt); // Array with all payments dispatching + + $postactionmessages[] = 'Payment was done in a different currency that currency expected of company'; + $ispostactionok = -1; + $error++; + } + $paiement->paiementid = $paymentTypeId; + $paiement->num_payment = ''; + $paiement->note_public = 'Online payment ' . dol_print_date($now, 'standard') . ' from ' . $ipaddress; + $paiement->ext_payment_id = $TRANSACTIONID; // pi_... for Stripe, ... + $paiement->ext_payment_site = $service; // 'StripeLive' or 'Stripe', or ... + + if (!$error) { + $paiement_id = $paiement->create($user, 1); // This include closing invoices and regenerating documents + if ($paiement_id < 0) { + $postactionmessages[] = $paiement->error . ' ' . join("
\n", $paiement->errors); + $ispostactionok = -1; + $error++; + } else { + $postactionmessages[] = 'Payment created'; + $ispostactionok = 1; + } + } + + if (!$error && isModEnabled("banque")) { + $bankaccountid = 0; + if ($paymentmethod == 'paybox') $bankaccountid = $conf->global->PAYBOX_BANK_ACCOUNT_FOR_PAYMENTS; + elseif ($paymentmethod == 'paypal') $bankaccountid = $conf->global->PAYPAL_BANK_ACCOUNT_FOR_PAYMENTS; + elseif ($paymentmethod == 'stripe') $bankaccountid = $conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS; + + if ($bankaccountid > 0) { + $label = '(CustomerInvoicePayment)'; + if ($object->type == Facture::TYPE_CREDIT_NOTE) $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note + $result = $paiement->addPaymentToBank($user, 'payment', $label, $bankaccountid, '', ''); + if ($result < 0) { + $postactionmessages[] = $paiement->error . ' ' . join("
\n", $paiement->errors); + $ispostactionok = -1; + $error++; + } else { + $postactionmessages[] = 'Bank transaction of payment created'; + $ispostactionok = 1; + } + } else { + $postactionmessages[] = 'Setup of bank account to use in module ' . $paymentmethod . ' was not set. No way to record the payment.'; + $ispostactionok = -1; + $error++; + } + } + + if (!$error) { + $db->commit(); + } else { + $db->rollback(); + } + } else { + $postactionmessages[] = 'Failed to create invoice form contract ' . $tmptag['CON'] . ' and col '. $tmptag['COL'] .'.'; + $ispostactionok = -1; + } + } else { + $postactionmessages[] = 'Failed to get a valid value for "amount paid" (' . $FinalPaymentAmt . ') or "payment type id" (' . $paymentTypeId . ') to record the payment of contract ' . $tmptag['CON'] . ' and col '. $tmptag['COL'] .'. Maybe payment was already recorded.'; + $ispostactionok = -1; + } + } else { + $postactionmessages[] = 'Invoice module is not enable'; + $ispostactionok = -1; + } + } else { + $postactionmessages[] = 'Contract paid ' . $tmptag['CON'] . ' was not found for col '. $tmptag['COL'] .'.'; + $ispostactionok = -1; + } + } + else { // Nothing done } } From 53d2f8eaee539dbe5aabeea25c84a2cdd48a006b Mon Sep 17 00:00:00 2001 From: tnegre Date: Fri, 21 Apr 2023 15:51:25 +0200 Subject: [PATCH 2/2] pay whole contract --- htdocs/public/payment/paymentok.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/htdocs/public/payment/paymentok.php b/htdocs/public/payment/paymentok.php index 0c731d99ca4..0f43a2ec454 100644 --- a/htdocs/public/payment/paymentok.php +++ b/htdocs/public/payment/paymentok.php @@ -1579,7 +1579,7 @@ if ($ispaymentok) { $postactionmessages[] = 'Invoice paid '.$tmptag['ATT'].' was not found'; $ispostactionok = -1; } - } elseif (array_key_exists('CON', $tmptag) && array_key_exists('COL', $tmptag) && $tmptag['CON'] > 0 && $tmptag['COL'] > 0) { + } elseif (array_key_exists('CON', $tmptag) && $tmptag['CON'] > 0) { include_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php'; $object = new Contrat($db); $result = $object->fetch((int) $tmptag['CON']); @@ -1613,13 +1613,14 @@ if ($ispaymentok) { } $currencyCodeType = $_SESSION['currencyCodeType']; + $contract_lines = (array_key_exists('COL', $tmptag) && $tmptag['COL'] > 0) ? $tmptag['COL'] : null; // Do action only if $FinalPaymentAmt is set (session variable is cleaned after this page to avoid duplicate actions when page is POST a second time) if (isModEnabled('facture')) { if (!empty($FinalPaymentAmt) && $paymentTypeId > 0 ) { include_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php'; $invoice = new Facture($db); - $result = $invoice->createFromContract($object, $user, array((int) $tmptag['COL'])); + $result = $invoice->createFromContract($object, $user, array((int) $contract_lines)); if ($result > 0) { // $object->classifyBilled($user); $invoice->validate($user); @@ -1685,11 +1686,13 @@ if ($ispaymentok) { $db->rollback(); } } else { - $postactionmessages[] = 'Failed to create invoice form contract ' . $tmptag['CON'] . ' and col '. $tmptag['COL'] .'.'; + $msg = 'Failed to create invoice form contract ' . $tmptag['CON']; + if (!empty($cols)) $msg .= ' and col '. $cols .'.'; + $postactionmessages[] = $msg; $ispostactionok = -1; } } else { - $postactionmessages[] = 'Failed to get a valid value for "amount paid" (' . $FinalPaymentAmt . ') or "payment type id" (' . $paymentTypeId . ') to record the payment of contract ' . $tmptag['CON'] . ' and col '. $tmptag['COL'] .'. Maybe payment was already recorded.'; + $postactionmessages[] = 'Failed to get a valid value for "amount paid" (' . $FinalPaymentAmt . ') or "payment type id" (' . $paymentTypeId . ') to record the payment of contract ' . $tmptag['CON'] .'. Maybe payment was already recorded.'; $ispostactionok = -1; } } else { @@ -1697,7 +1700,9 @@ if ($ispaymentok) { $ispostactionok = -1; } } else { - $postactionmessages[] = 'Contract paid ' . $tmptag['CON'] . ' was not found for col '. $tmptag['COL'] .'.'; + $msg = 'Contract paid ' . $tmptag['CON'] . ' was not found'; + if (!empty($cols)) $msg .= ' for col '.$tmptag['COL'] .'.'; + $postactionmessages[] = $msg; $ispostactionok = -1; } }