From 65629711d0722a96f5061b7b8e5dffa224cdb79b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 25 Aug 2021 14:53:14 +0200 Subject: [PATCH] FIX #18484 --- htdocs/core/actions_massactions.inc.php | 40 +++--- htdocs/core/class/commonobject.class.php | 6 +- htdocs/langs/en_US/orders.lang | 1 + htdocs/reception/list.php | 157 +++++++++++++++-------- 4 files changed, 124 insertions(+), 80 deletions(-) diff --git a/htdocs/core/actions_massactions.inc.php b/htdocs/core/actions_massactions.inc.php index cf412223838..c2538421bfe 100644 --- a/htdocs/core/actions_massactions.inc.php +++ b/htdocs/core/actions_massactions.inc.php @@ -631,6 +631,8 @@ if ($massaction == 'confirm_createbills') { // Create bills from orders. $createbills_onebythird = GETPOST('createbills_onebythird', 'int'); $validate_invoices = GETPOST('validate_invoices', 'int'); + $errors = array(); + $TFact = array(); $TFactThird = array(); @@ -645,18 +647,19 @@ if ($massaction == 'confirm_createbills') { // Create bills from orders. if ($cmd->fetch($id_order) <= 0) { continue; } + $cmd->fetch_thirdparty(); $objecttmp = new Facture($db); if (!empty($createbills_onebythird) && !empty($TFactThird[$cmd->socid])) { - $objecttmp = $TFactThird[$cmd->socid]; // If option "one bill per third" is set, we use already created order. + // If option "one bill per third" is set, and an invoice for this thirdparty was already created, we re-use it. + $objecttmp = $TFactThird[$cmd->socid]; } else { - // Load extrafields of order - $cmd->fetch_optionals(); - + // If we want one invoice per order or if there is no first invoice yet for this thirdparty. $objecttmp->socid = $cmd->socid; $objecttmp->type = $objecttmp::TYPE_STANDARD; - $objecttmp->cond_reglement_id = $cmd->cond_reglement_id; - $objecttmp->mode_reglement_id = $cmd->mode_reglement_id; + $objecttmp->cond_reglement_id = ($cmd->cond_reglement_id || $cmd->thirdparty->cond_reglement_id); + $objecttmp->mode_reglement_id = ($cmd->mode_reglement_id || $cmd->thirdparty->mode_reglement_id); + $objecttmp->fk_project = $cmd->fk_project; $objecttmp->multicurrency_code = $cmd->multicurrency_code; if (empty($createbills_onebythird)) { @@ -680,23 +683,20 @@ if ($massaction == 'confirm_createbills') { // Create bills from orders. $nb_bills_created++; $lastref = $objecttmp->ref; $lastid = $objecttmp->id; + + $TFactThird[$cmd->socid] = $objecttmp; + } else { + $langs->load("errors"); + $errors[] = $cmd->ref.' : '.$langs->trans($objecttmp->error); + $error++; } } if ($objecttmp->id > 0) { - $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element ("; - $sql .= "fk_source"; - $sql .= ", sourcetype"; - $sql .= ", fk_target"; - $sql .= ", targettype"; - $sql .= ") VALUES ("; - $sql .= $id_order; - $sql .= ", '".$db->escape($objecttmp->origin)."'"; - $sql .= ", ".$objecttmp->id; - $sql .= ", '".$db->escape($objecttmp->element)."'"; - $sql .= ")"; + $res = $objecttmp->add_object_linked($objecttmp->origin, $id_order); - if (!$db->query($sql)) { + if ($res == 0) { + $errors[] = $objecttmp->error; $error++; } @@ -845,7 +845,6 @@ if ($massaction == 'confirm_createbills') { // Create bills from orders. } $id = $objecttmp->id; // For builddoc action - $object = $objecttmp; // Builddoc $donotredirect = 1; @@ -854,7 +853,7 @@ if ($massaction == 'confirm_createbills') { // Create bills from orders. // Call action to build doc $savobject = $object; - $object = $objecttmp; + $object = $objecttmp; include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; $object = $savobject; } @@ -949,6 +948,7 @@ if ($massaction == 'confirm_createbills') { // Create bills from orders. exit; } else { $db->rollback(); + $action = 'create'; $_GET["origin"] = $_POST["origin"]; $_GET["originid"] = $_POST["originid"]; diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 1e28afd41e0..5df73f49f5c 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -3577,7 +3577,7 @@ abstract class CommonObject * @param string $origin Linked element type * @param int $origin_id Linked element id * @param User $f_user User that create - * @param int $notrigger 1=Does not execute triggers, 0= execute triggers + * @param int $notrigger 1=Does not execute triggers, 0=execute triggers * @return int <=0 if KO, >0 if OK * @see fetchObjectLinked(), updateObjectLinked(), deleteObjectLinked() */ @@ -3611,9 +3611,9 @@ abstract class CommonObject $sql .= ", fk_target"; $sql .= ", targettype"; $sql .= ") VALUES ("; - $sql .= $origin_id; + $sql .= ((int) $origin_id); $sql .= ", '" . $this->db->escape($origin) . "'"; - $sql .= ", " . $this->id; + $sql .= ", " . ((int) $this->id); $sql .= ", '" . $this->db->escape($this->element) . "'"; $sql .= ")"; diff --git a/htdocs/langs/en_US/orders.lang b/htdocs/langs/en_US/orders.lang index 5dab5b99bf1..9018db40a5b 100644 --- a/htdocs/langs/en_US/orders.lang +++ b/htdocs/langs/en_US/orders.lang @@ -151,6 +151,7 @@ PDFEdisonDescription=A simple order model PDFProformaDescription=A complete Proforma invoice template CreateInvoiceForThisCustomer=Bill orders CreateInvoiceForThisSupplier=Bill orders +CreateInvoiceForThisReceptions=Bill receptions NoOrdersToInvoice=No orders billable CloseProcessedOrdersAutomatically=Classify "Processed" all selected orders. OrderCreation=Order creation diff --git a/htdocs/reception/list.php b/htdocs/reception/list.php index dd9f365c39a..e4b519517ea 100644 --- a/htdocs/reception/list.php +++ b/htdocs/reception/list.php @@ -171,87 +171,102 @@ if (empty($reshook)) { $createbills_onebythird = GETPOST('createbills_onebythird', 'int'); $validate_invoices = GETPOST('validate_invoices', 'int'); + $errors = array(); + $TFact = array(); $TFactThird = array(); $nb_bills_created = 0; + $lastid= 0; + $lastref = ''; $db->begin(); - $errors = array(); + foreach ($receptions as $id_reception) { $rcp = new Reception($db); - // On ne facture que les réceptions validées - if ($rcp->fetch($id_reception) <= 0 || $rcp->statut != 1) { + // We only invoice reception that are validated + if ($rcp->fetch($id_reception) <= 0 || $rcp->statut != $rcp::STATUS_VALIDATED) { $errors[] = $langs->trans('StatusOfRefMustBe', $rcp->ref, $langs->transnoentities("StatusSupplierOrderValidatedShort")); $error++; continue; } - $object = new FactureFournisseur($db); + $objecttmp = new FactureFournisseur($db); if (!empty($createbills_onebythird) && !empty($TFactThird[$rcp->socid])) { - $object = $TFactThird[$rcp->socid]; // If option "one bill per third" is set, we use already created reception. - if (empty($object->rowid) && $object->id != null) { - $object->rowid = $object->id; - } - if (!empty($object->rowid)) { - $object->fetchObjectLinked(); - } - $rcp->fetchObjectLinked(); + // If option "one bill per third" is set, and an invoice for this thirdparty was already created, we re-use it. + $objecttmp = $TFactThird[$rcp->socid]; - if (count($rcp->linkedObjectsIds['reception']) > 0) { - foreach ($rcp->linkedObjectsIds['reception'] as $key => $value) { - if (empty($object->linkedObjectsIds['reception']) || !in_array($value, $object->linkedObjectsIds['reception'])) { //Dont try to link if already linked - $object->add_object_linked('reception', $value); // add supplier order linked object + // Add all links of this new reception to the existing invoice + $objecttmp->fetchObjectLinked(); + $rcp->fetchObjectLinked(); + if (count($rcp->linkedObjectsIds['order_supplier']) > 0) { + foreach ($rcp->linkedObjectsIds['order_supplier'] as $key => $value) { + if (empty($objecttmp->linkedObjectsIds['order_supplier']) || !in_array($value, $objecttmp->linkedObjectsIds['order_supplier'])) { //Dont try to link if already linked + $objecttmp->add_object_linked('order_supplier', $value); // add supplier order linked object } } } } else { - $object->socid = $rcp->socid; - $object->type = FactureFournisseur::TYPE_STANDARD; - $object->cond_reglement_id = $rcp->thirdparty->cond_reglement_supplier_id; - $object->mode_reglement_id = $rcp->thirdparty->mode_reglement_supplier_id; - $object->fk_account = !empty($rcp->thirdparty->fk_account) ? $rcp->thirdparty->fk_account : 0; - $object->remise_percent = !empty($rcp->thirdparty->remise_percent) ? $rcp->thirdparty->remise_percent : 0; - $object->remise_absolue = !empty($rcp->thirdparty->remise_absolue) ? $rcp->thirdparty->remise_absolue : 0; + // If we want one invoice per reception or if there is no first invoice yet for this thirdparty. + $objecttmp->socid = $rcp->socid; + $objecttmp->type = $objecttmp::TYPE_STANDARD; + $objecttmp->cond_reglement_id = $rcp->cond_reglement_id || $rcp->thirdparty->cond_reglement_supplier_id; + $objecttmp->mode_reglement_id = $rcp->mode_reglement_id || $rcp->thirdparty->mode_reglement_supplier_id; - $object->fk_project = $rcp->fk_project; - $object->ref_supplier = $rcp->ref_supplier; + $objecttmp->fk_account = !empty($rcp->thirdparty->fk_account) ? $rcp->thirdparty->fk_account : 0; + $objecttmp->remise_percent = !empty($rcp->thirdparty->remise_percent) ? $rcp->thirdparty->remise_percent : 0; + $objecttmp->remise_absolue = !empty($rcp->thirdparty->remise_absolue) ? $rcp->thirdparty->remise_absolue : 0; - $datefacture = dol_mktime(12, 0, 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')); - if (empty($datefacture)) { - $datefacture = dol_mktime(date("h"), date("M"), 0, date("m"), date("d"), date("Y")); + $objecttmp->fk_project = $rcp->fk_project; + //$objecttmp->multicurrency_code = $rcp->multicurrency_code; + if (empty($createbills_onebythird)) { + $objecttmp->ref_supplier = $rcp->ref; + } else { + // Set a unique value for the invoice for the n reception + $objecttmp->ref_supplier = $langs->trans("Reception").' '.dol_print_date(dol_now(), 'dayhourlog').'-'.$rcp->socid; } - $object->date = $datefacture; - $object->origin = 'reception'; - $object->origin_id = $id_reception; + $datefacture = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); + if (empty($datefacture)) { + $datefacture = dol_now(); + } + $objecttmp->date = $datefacture; + $objecttmp->origin = 'reception'; + $objecttmp->origin_id = $id_reception; + + $objecttmp->array_options = $rcp->array_options; // Copy extrafields + + // Set $objecttmp->linked_objects with all links order_supplier existing on reception, so same links will be added to the generated supplier invoice $rcp->fetchObjectLinked(); - if (count($rcp->linkedObjectsIds['reception']) > 0) { - foreach ($rcp->linkedObjectsIds['reception'] as $key => $value) { - $object->linked_objects['reception'] = $value; + if (count($rcp->linkedObjectsIds['order_supplier']) > 0) { + foreach ($rcp->linkedObjectsIds['order_supplier'] as $key => $value) { + $objecttmp->linked_objects['order_supplier'] = $value; } } - $res = $object->create($user); - //var_dump($object->error);exit; + $res = $objecttmp->create($user); // This should create the supplier invoice + links into $objecttmp->linked_objects + add a link to ->origin_id + + //var_dump($objecttmp->error);exit; if ($res > 0) { $nb_bills_created++; - $object->id = $res; + $lastref = $objecttmp->ref; + $lastid = $objecttmp->id; + + $TFactThird[$rcp->socid] = $objecttmp; } else { - $errors[] = $rcp->ref.' : '.$langs->trans($object->error); + $langs->load("errors"); + $errors[] = $rcp->ref.' : '.$langs->trans($objecttmp->error); $error++; } } - if ($object->id > 0) { - if (!empty($createbills_onebythird) && !empty($TFactThird[$rcp->socid])) { //cause function create already add object linked for facturefournisseur - $res = $object->add_object_linked($object->origin, $id_reception); + if ($objecttmp->id > 0) { + $res = $objecttmp->add_object_linked($objecttmp->origin, $id_reception); - if ($res == 0) { - $errors[] = $object->error; - $error++; - } + if ($res == 0) { + $errors[] = $objecttmp->error; + $error++; } if (!$error) { @@ -266,10 +281,15 @@ if (empty($reshook)) { for ($i = 0; $i < $num; $i++) { $desc = ($lines[$i]->desc ? $lines[$i]->desc : $lines[$i]->libelle); + // If we build one invoice for several reception, we must put the ref of reception on the invoice line + if (!empty($createbills_onebythird)) { + $desc = dol_concatdesc($desc, $langs->trans("Reception").' '.$rcp->ref.' - '.dol_print_date($rcp->date, 'day')); + } + if ($lines[$i]->subprice < 0) { // Negative line, we create a discount line $discount = new DiscountAbsolute($db); - $discount->fk_soc = $object->socid; + $discount->fk_soc = $objecttmp->socid; $discount->amount_ht = abs($lines[$i]->total_ht); $discount->amount_tva = abs($lines[$i]->total_tva); $discount->amount_ttc = abs($lines[$i]->total_ttc); @@ -278,7 +298,7 @@ if (empty($reshook)) { $discount->description = $desc; $discountid = $discount->create($user); if ($discountid > 0) { - $result = $object->insert_discount($discountid); + $result = $objecttmp->insert_discount($discountid); //$result=$discount->link_to_invoice($lineid,$id); } else { setEventMessages($discount->error, $discount->errors, 'errors'); @@ -314,7 +334,16 @@ if (empty($reshook)) { if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) { $fk_parent_line = 0; } - $result = $object->addline( + + // Extrafields + if (method_exists($lines[$i], 'fetch_optionals')) { + $lines[$i]->fetch_optionals(); + $array_options = $lines[$i]->array_options; + } + + $objecttmp->context['createfromclone']; + + $result = $objecttmp->addline( $desc, $lines[$i]->subprice, $lines[$i]->tva_tx, @@ -359,9 +388,9 @@ if (empty($reshook)) { //$rcp->classifyBilled($user); // Disabled. This behavior must be set or not using the workflow module. if (!empty($createbills_onebythird) && empty($TFactThird[$rcp->socid])) { - $TFactThird[$rcp->socid] = $object; + $TFactThird[$rcp->socid] = $objecttmp; } else { - $TFact[$object->id] = $object; + $TFact[$objecttmp->id] = $objecttmp; } } @@ -371,21 +400,27 @@ if (empty($reshook)) { if (!$error && $validate_invoices) { $massaction = $action = 'builddoc'; - foreach ($TAllFact as &$object) { - $result = $object->validate($user); + foreach ($TAllFact as &$objecttmp) { + $result = $objecttmp->validate($user); if ($result <= 0) { $error++; - setEventMessages($object->error, $object->errors, 'errors'); + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); break; } - $id = $object->id; // For builddoc action + $id = $objecttmp->id; // For builddoc action + $object =$objecttmp; // Fac builddoc $donotredirect = 1; $upload_dir = $conf->fournisseur->facture->dir_output; $permissiontoadd = ($user->rights->fournisseur->facture->creer || $user->rights->supplier_invoice->creer); + + // Call action to build doc + $savobject = $object; + $object = $objecttmp; include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; + $object = $savobject; } $massaction = $action = 'confirm_createbills'; @@ -393,9 +428,17 @@ if (empty($reshook)) { if (!$error) { $db->commit(); - setEventMessage($langs->trans('BillCreated', $nb_bills_created)); + + if ($nb_bills_created == 1) { + $texttoshow = $langs->trans('BillXCreated', '{s1}'); + $texttoshow = str_replace('{s1}', ''.$lastref.'', $texttoshow); + setEventMessages($texttoshow, null, 'mesgs'); + } else { + setEventMessages($langs->trans('BillCreated', $nb_bills_created), null, 'mesgs'); + } } else { $db->rollback(); + $action = 'create'; $_GET["origin"] = $_POST["origin"]; $_GET["originid"] = $_POST["originid"]; @@ -597,7 +640,7 @@ $arrayofmassactions = array( ); if ($user->rights->fournisseur->facture->creer || $user->rights->supplier_invoice->creer) { - $arrayofmassactions['createbills'] = $langs->trans("CreateInvoiceForThisSupplier"); + $arrayofmassactions['createbills'] = $langs->trans("CreateInvoiceForThisReceptions"); } if ($massaction == 'createbills') { $arrayofmassactions = array(); @@ -656,7 +699,7 @@ if ($massaction == 'createbills') { print '
'; print '
'; - print ' '; + print ' '; print ''; print '
'; print '
';