dolibarr 21.0.0-beta
interface_20_modWorkflow_WorkflowManager.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2010 Regis Houssin <regis.houssin@inodbox.com>
3 * Copyright (C) 2011-2017 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2014 Marcos GarcĂ­a <marcosgdf@gmail.com>
5 * Copyright (C) 2022-2024 Ferran Marcet <fmarcet@2byte.es>
6 * Copyright (C) 2023 Alexandre Janniaux <alexandre.janniaux@gmail.com>
7 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
29require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
30
31
37{
43 public function __construct($db)
44 {
45 $this->db = $db;
46
47 $this->name = preg_replace('/^Interface/i', '', get_class($this));
48 $this->family = "core";
49 $this->description = "Triggers of this module allows to manage workflows";
50 $this->version = self::VERSIONS['prod'];
51 $this->picto = 'technic';
52 }
53
65 public function runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
66 {
67 if (empty($conf->workflow) || empty($conf->workflow->enabled)) {
68 return 0; // Module not active, we do nothing
69 }
70
71 $ret = 0;
72
73 // Proposals to order
74 if ($action == 'PROPAL_CLOSE_SIGNED' && $object instanceof Propal) {
75 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
76 if (isModEnabled('order') && getDolGlobalString('WORKFLOW_PROPAL_AUTOCREATE_ORDER')) {
77 $object->fetchObjectLinked();
78 if (!empty($object->linkedObjectsIds['commande'])) {
79 if (empty($object->context['closedfromonlinesignature'])) {
80 $langs->load("orders");
81 setEventMessages($langs->trans("OrderExists"), null, 'warnings');
82 }
83 return $ret;
84 }
85
86 include_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
87 $newobject = new Commande($this->db);
88
89 $newobject->context['createfrompropal'] = 'createfrompropal';
90 $newobject->context['origin'] = $object->element;
91 $newobject->context['origin_id'] = $object->id;
92
93 $ret = $newobject->createFromProposal($object, $user);
94 if ($ret < 0) {
95 $this->setErrorsFromObject($newobject);
96 }
97
98 $object->clearObjectLinkedCache();
99
100 return (int) $ret;
101 }
102 }
103
104 // Order to invoice
105 if ($action == 'ORDER_CLOSE') {
106 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
107 if (isModEnabled('invoice') && getDolGlobalString('WORKFLOW_ORDER_AUTOCREATE_INVOICE')) {
108 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
109 '@phan-var-force Facture $object';
110 $newobject = new Facture($this->db);
111
112 $newobject->context['createfromorder'] = 'createfromorder';
113 $newobject->context['origin'] = $object->element;
114 $newobject->context['origin_id'] = $object->id;
115
116 $ret = $newobject->createFromOrder($object, $user);
117 if ($ret < 0) {
118 $this->setErrorsFromObject($newobject);
119 } else {
120 if (empty($object->fk_account) && !empty($object->thirdparty->fk_account) && !getDolGlobalInt('BANK_ASK_PAYMENT_BANK_DURING_ORDER')) {
121 $res = $newobject->setBankAccount($object->thirdparty->fk_account, true, $user);
122 if ($ret < 0) {
123 $this->setErrorsFromObject($newobject);
124 }
125 }
126 }
127
128 $object->clearObjectLinkedCache();
129
130 return $ret;
131 }
132 }
133
134 // Order classify billed proposal
135 if ($action == 'ORDER_CLASSIFY_BILLED') {
136 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
137 if (isModEnabled("propal") && !empty($conf->workflow->enabled) && getDolGlobalString('WORKFLOW_ORDER_CLASSIFY_BILLED_PROPAL')) {
138 $object->fetchObjectLinked(0, 'propal', $object->id, $object->element);
139 if (!empty($object->linkedObjects['propal'])) {
140 $totalonlinkedelements = 0;
141 foreach ($object->linkedObjects['propal'] as $element) {
142 if ($element->statut == Propal::STATUS_SIGNED || $element->statut == Propal::STATUS_BILLED) {
143 $totalonlinkedelements += $element->total_ht;
144 }
145 }
146 dol_syslog("Amount of linked proposals = ".$totalonlinkedelements.", of order = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht));
147 if ($this->shouldClassify($conf, $totalonlinkedelements, $object->total_ht)) {
148 foreach ($object->linkedObjects['propal'] as $element) {
149 $ret = $element->classifyBilled($user);
150 }
151 }
152 }
153 return $ret;
154 }
155 }
156
157 // classify billed order & billed propososal
158 if ($action == 'BILL_VALIDATE') {
159 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
160
161 // First classify billed the order to allow the proposal classify process
162 if (isModEnabled('order') && !empty($conf->workflow->enabled) && getDolGlobalString('WORKFLOW_INVOICE_AMOUNT_CLASSIFY_BILLED_ORDER')) {
163 $object->fetchObjectLinked(0, 'commande', $object->id, $object->element);
164 if (!empty($object->linkedObjects['commande'])) {
165 $totalonlinkedelements = 0;
166 foreach ($object->linkedObjects['commande'] as $element) {
167 if ($element->statut == Commande::STATUS_VALIDATED || $element->statut == Commande::STATUS_SHIPMENTONPROCESS || $element->statut == Commande::STATUS_CLOSED) {
168 $totalonlinkedelements += $element->total_ht;
169 }
170 }
171 dol_syslog("Amount of linked orders = ".$totalonlinkedelements.", of invoice = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht));
172 if ($this->shouldClassify($conf, $totalonlinkedelements, $object->total_ht)) {
173 foreach ($object->linkedObjects['commande'] as $element) {
174 $ret = $element->classifyBilled($user);
175 }
176 }
177 }
178 }
179
180 // Second classify billed the proposal.
181 if (isModEnabled("propal") && !empty($conf->workflow->enabled) && getDolGlobalString('WORKFLOW_INVOICE_CLASSIFY_BILLED_PROPAL')) {
182 $object->fetchObjectLinked(0, 'propal', $object->id, $object->element);
183 if (!empty($object->linkedObjects['propal'])) {
184 $totalonlinkedelements = 0;
185 foreach ($object->linkedObjects['propal'] as $element) {
186 if ($element->statut == Propal::STATUS_SIGNED || $element->statut == Propal::STATUS_BILLED) {
187 $totalonlinkedelements += $element->total_ht;
188 }
189 }
190 dol_syslog("Amount of linked proposals = ".$totalonlinkedelements.", of invoice = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht));
191 if ($this->shouldClassify($conf, $totalonlinkedelements, $object->total_ht)) {
192 foreach ($object->linkedObjects['propal'] as $element) {
193 $ret = $element->classifyBilled($user);
194 }
195 }
196 }
197 }
198
199 // Set shipment to "Closed" if WORKFLOW_SHIPPING_CLASSIFY_CLOSED_INVOICE is set (deprecated, has been replaced with WORKFLOW_SHIPPING_CLASSIFY_BILLED_INVOICE instead))
200 if (isModEnabled("shipping") && !empty($conf->workflow->enabled) && getDolGlobalString('WORKFLOW_SHIPPING_CLASSIFY_CLOSED_INVOICE')) {
201 $object->fetchObjectLinked(0, 'shipping', $object->id, $object->element);
202 if (!empty($object->linkedObjects['shipping'])) {
203 $totalonlinkedelements = 0;
204 foreach ($object->linkedObjects['shipping'] as $element) {
205 if ($element->statut == Expedition::STATUS_VALIDATED) {
206 $totalonlinkedelements += $element->total_ht;
207 }
208 }
209 dol_syslog("Amount of linked shipment = ".$totalonlinkedelements.", of invoice = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht), LOG_DEBUG);
210 if ($totalonlinkedelements == $object->total_ht) {
211 foreach ($object->linkedObjects['shipping'] as $element) {
212 $ret = $element->setClosed();
213 if ($ret < 0) {
214 return (int) $ret;
215 }
216 }
217 }
218 }
219 }
220
221 if (isModEnabled("shipping") && !empty($conf->workflow->enabled) && getDolGlobalString('WORKFLOW_SHIPPING_CLASSIFY_BILLED_INVOICE')) {
222 $object->fetchObjectLinked(0, 'shipping', $object->id, $object->element);
223 if (!empty($object->linkedObjects['shipping'])) {
224 $totalonlinkedelements = 0;
225 foreach ($object->linkedObjects['shipping'] as $element) {
226 if ($element->statut == Expedition::STATUS_VALIDATED || $element->statut == Expedition::STATUS_CLOSED) {
227 $totalonlinkedelements += $element->total_ht;
228 }
229 }
230 dol_syslog("Amount of linked shipment = ".$totalonlinkedelements.", of invoice = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht), LOG_DEBUG);
231 if ($totalonlinkedelements == $object->total_ht) {
232 foreach ($object->linkedObjects['shipping'] as $element) {
233 $ret = $element->setBilled();
234 if ($ret < 0) {
235 return (int) $ret;
236 }
237 }
238 }
239 }
240 }
241
242 // First classify billed the order to allow the proposal classify process
243 if (isModEnabled('order') && isModEnabled('workflow') && getDolGlobalString('WORKFLOW_SUM_INVOICES_AMOUNT_CLASSIFY_BILLED_ORDER')) {
244 $object->fetchObjectLinked(0, 'commande', $object->id, $object->element);
245 if (!empty($object->linkedObjects['commande']) && count($object->linkedObjects['commande']) == 1) { // If the invoice has only 1 source order
246 $orderLinked = reset($object->linkedObjects['commande']);
247 $orderLinked->fetchObjectLinked($orderLinked->id, '', $orderLinked->element);
248 if (count($orderLinked->linkedObjects['facture']) >= 1) {
249 $totalHTInvoices = 0;
250 $areAllInvoicesValidated = true;
251 foreach ($orderLinked->linkedObjects['facture'] as $key => $invoice) {
252 if ($invoice->statut == Facture::STATUS_VALIDATED || $invoice->statut == Facture::STATUS_CLOSED || $object->id == $invoice->id) {
253 $totalHTInvoices += (float) $invoice->total_ht;
254 } else {
255 $areAllInvoicesValidated = false;
256 break;
257 }
258 }
259 if ($areAllInvoicesValidated) {
260 $isSameTotal = (price2num($totalHTInvoices, 'MT') == price2num($orderLinked->total_ht, 'MT'));
261 dol_syslog("Amount of linked invoices = ".$totalHTInvoices.", of order = ".$orderLinked->total_ht.", isSameTotal = ".(string) $isSameTotal, LOG_DEBUG);
262 if ($isSameTotal) {
263 $ret = $orderLinked->classifyBilled($user);
264 if ($ret < 0) {
265 return $ret;
266 }
267 }
268 }
269 }
270 }
271 }
272 return $ret;
273 }
274
275 // classify billed order & billed proposal
276 if ($action == 'BILL_SUPPLIER_VALIDATE') {
277 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
278
279 // Firstly, we set to purchase order to "Billed" if WORKFLOW_INVOICE_AMOUNT_CLASSIFY_BILLED_SUPPLIER_ORDER is set.
280 // After we will set proposals
281 if ((isModEnabled("supplier_order") || isModEnabled("supplier_invoice")) && getDolGlobalString('WORKFLOW_INVOICE_AMOUNT_CLASSIFY_BILLED_SUPPLIER_ORDER')) {
282 $object->fetchObjectLinked(0, 'order_supplier', $object->id, $object->element);
283 if (!empty($object->linkedObjects['order_supplier'])) {
284 $totalonlinkedelements = 0;
285 foreach ($object->linkedObjects['order_supplier'] as $element) {
287 $totalonlinkedelements += $element->total_ht;
288 }
289 }
290 dol_syslog("Amount of linked orders = ".$totalonlinkedelements.", of invoice = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht));
291 if ($this->shouldClassify($conf, $totalonlinkedelements, $object->total_ht)) {
292 foreach ($object->linkedObjects['order_supplier'] as $element) {
293 $ret = $element->classifyBilled($user);
294 if ($ret < 0) {
295 return $ret;
296 }
297 }
298 }
299 }
300 }
301
302 // Secondly, we set to linked Proposal to "Billed" if WORKFLOW_INVOICE_CLASSIFY_BILLED_SUPPLIER_PROPOSAL is set.
303 if (isModEnabled('supplier_proposal') && getDolGlobalString('WORKFLOW_INVOICE_CLASSIFY_BILLED_SUPPLIER_PROPOSAL')) {
304 $object->fetchObjectLinked(0, 'supplier_proposal', $object->id, $object->element);
305 if (!empty($object->linkedObjects['supplier_proposal'])) {
306 $totalonlinkedelements = 0;
307 foreach ($object->linkedObjects['supplier_proposal'] as $element) {
308 if ($element->statut == SupplierProposal::STATUS_SIGNED || $element->statut == SupplierProposal::STATUS_CLOSE) {
309 $totalonlinkedelements += $element->total_ht;
310 }
311 }
312 dol_syslog("Amount of linked supplier proposals = ".$totalonlinkedelements.", of supplier invoice = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht));
313 if ($this->shouldClassify($conf, $totalonlinkedelements, $object->total_ht)) {
314 foreach ($object->linkedObjects['supplier_proposal'] as $element) {
315 $ret = $element->classifyBilled($user);
316 if ($ret < 0) {
317 return $ret;
318 }
319 }
320 }
321 }
322 }
323
324 // Set reception to "Closed" if WORKFLOW_RECEPTION_CLASSIFY_CLOSED_INVOICE is set (deprecated, WORKFLOW_RECEPTION_CLASSIFY_BILLED_INVOICE instead))
325 /*
326 if (isModEnabled("reception") && !empty($conf->workflow->enabled) && !empty($conf->global->WORKFLOW_RECEPTION_CLASSIFY_CLOSED_INVOICE)) {
327 $object->fetchObjectLinked('', 'reception', $object->id, $object->element);
328 if (!empty($object->linkedObjects['reception'])) {
329 $totalonlinkedelements = 0;
330 foreach ($object->linkedObjects['reception'] as $element) {
331 if ($element->statut == Reception::STATUS_VALIDATED || $element->statut == Reception::STATUS_CLOSED) {
332 $totalonlinkedelements += $element->total_ht;
333 }
334 }
335 dol_syslog("Amount of linked reception = ".$totalonlinkedelements.", of invoice = ".$object->total_ht.", egality is ".((string) $totalonlinkedelements == (string) $object->total_ht), LOG_DEBUG);
336 if ( (string) $totalonlinkedelements == (string) $object->total_ht) {
337 foreach ($object->linkedObjects['reception'] as $element) {
338 $ret = $element->setClosed();
339 if ($ret < 0) {
340 return $ret;
341 }
342 }
343 }
344 }
345 }
346 */
347
348 // Then set reception to "Billed" if WORKFLOW_RECEPTION_CLASSIFY_BILLED_INVOICE is set
349 if (isModEnabled("reception") && !empty($conf->workflow->enabled) && getDolGlobalString('WORKFLOW_RECEPTION_CLASSIFY_BILLED_INVOICE')) {
350 $object->fetchObjectLinked(0, 'reception', $object->id, $object->element);
351 if (!empty($object->linkedObjects['reception'])) {
352 $totalonlinkedelements = 0;
353 foreach ($object->linkedObjects['reception'] as $element) {
354 if ($element->statut == Reception::STATUS_VALIDATED || $element->statut == Reception::STATUS_CLOSED) {
355 $totalonlinkedelements += $element->total_ht;
356 }
357 }
358 dol_syslog("Amount of linked reception = ".$totalonlinkedelements.", of invoice = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht), LOG_DEBUG);
359 if ($totalonlinkedelements == $object->total_ht) {
360 foreach ($object->linkedObjects['reception'] as $element) {
361 $ret = $element->setBilled();
362 if ($ret < 0) {
363 return $ret;
364 }
365 }
366 }
367 }
368 }
369
370 return $ret;
371 }
372
373 // Invoice classify billed order
374 if ($action == 'BILL_PAYED') {
375 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
376
377 if (isModEnabled('order') && getDolGlobalString('WORKFLOW_INVOICE_CLASSIFY_BILLED_ORDER')) {
378 $object->fetchObjectLinked(0, 'commande', $object->id, $object->element);
379 if (!empty($object->linkedObjects['commande'])) {
380 $totalonlinkedelements = 0;
381 foreach ($object->linkedObjects['commande'] as $element) {
382 if ($element->statut == Commande::STATUS_VALIDATED || $element->statut == Commande::STATUS_SHIPMENTONPROCESS || $element->statut == Commande::STATUS_CLOSED) {
383 $totalonlinkedelements += $element->total_ht;
384 }
385 }
386 dol_syslog("Amount of linked orders = ".$totalonlinkedelements.", of invoice = ".$object->total_ht.", egality is ".json_encode($totalonlinkedelements == $object->total_ht));
387 if ($this->shouldClassify($conf, $totalonlinkedelements, $object->total_ht)) {
388 foreach ($object->linkedObjects['commande'] as $element) {
389 $ret = $element->classifyBilled($user);
390 }
391 }
392 }
393 return $ret;
394 }
395 }
396
397 // If we validate or close a shipment
398 if (($action == 'SHIPPING_VALIDATE') || ($action == 'SHIPPING_CLOSED')) {
399 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
400
401 if (isModEnabled('order') && isModEnabled("shipping") && !empty($conf->workflow->enabled) &&
402 (
403 (getDolGlobalString('WORKFLOW_ORDER_CLASSIFY_SHIPPED_SHIPPING') && ($action == 'SHIPPING_VALIDATE')) ||
404 (getDolGlobalString('WORKFLOW_ORDER_CLASSIFY_SHIPPED_SHIPPING_CLOSED') && ($action == 'SHIPPING_CLOSED'))
405 )
406 ) {
407 $qtyshipped = array();
408 $qtyordred = array();
409
410 // The original sale order is id in $object->origin_id
411 // Find all shipments on sale order origin
412
413 if (in_array($object->origin, array('order', 'commande')) && $object->origin_id > 0) {
414 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
415 $order = new Commande($this->db);
416 $ret = $order->fetch($object->origin_id);
417 if ($ret < 0) {
418 $this->setErrorsFromObject($order);
419 return $ret;
420 }
421 $ret = $order->fetchObjectLinked($order->id, 'commande', null, 'shipping');
422 if ($ret < 0) {
423 $this->setErrorsFromObject($order);
424 return $ret;
425 }
426 //Build array of quantity shipped by product for an order
427 if (is_array($order->linkedObjects) && count($order->linkedObjects) > 0) {
428 foreach ($order->linkedObjects as $type => $shipping_array) {
429 if ($type != 'shipping' || !is_array($shipping_array) || count($shipping_array) == 0) {
430 continue;
431 }
433 foreach ($shipping_array as $shipping) {
434 if ($shipping->status <= 0 || !is_array($shipping->lines) || count($shipping->lines) == 0) {
435 continue;
436 }
437
438 foreach ($shipping->lines as $shippingline) {
439 if (isset($qtyshipped[$shippingline->fk_product])) {
440 $qtyshipped[$shippingline->fk_product] += $shippingline->qty;
441 } else {
442 $qtyshipped[$shippingline->fk_product] = $shippingline->qty;
443 }
444 }
445 }
446 }
447 }
448
449 //Build array of quantity ordered to be shipped
450 if (is_array($order->lines) && count($order->lines) > 0) {
451 foreach ($order->lines as $orderline) {
452 // Exclude lines not qualified for shipment, similar code is found into calcAndSetStatusDispatch() for vendors
453 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $orderline->product_type > 0) {
454 continue;
455 }
456 if (isset($qtyordred[$shippingline->fk_product])) {
457 $qtyordred[$orderline->fk_product] += $orderline->qty;
458 } else {
459 $qtyordred[$orderline->fk_product] = $orderline->qty;
460 }
461 }
462 }
463 //dol_syslog(var_export($qtyordred,true),LOG_DEBUG);
464 //dol_syslog(var_export($qtyshipped,true),LOG_DEBUG);
465 //Compare array
466 $diff_array = array_diff_assoc($qtyordred, $qtyshipped);
467 if (count($diff_array) == 0) {
468 //No diff => mean everything is shipped
469 $ret = $order->setStatut(Commande::STATUS_CLOSED, $object->origin_id, $object->origin, 'ORDER_CLOSE');
470 if ($ret < 0) {
471 $this->setErrorsFromObject($order);
472 return $ret;
473 }
474 }
475 }
476 }
477 }
478
479 // If we validate or close a shipment
480 if (($action == 'RECEPTION_VALIDATE') || ($action == 'RECEPTION_CLOSED')) {
481 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
482
483 if ((isModEnabled("fournisseur") || isModEnabled("supplier_order")) && isModEnabled("reception") && isModEnabled('workflow') &&
484 (
485 (getDolGlobalString('WORKFLOW_ORDER_CLASSIFY_RECEIVED_RECEPTION') && ($action == 'RECEPTION_VALIDATE')) ||
486 (getDolGlobalString('WORKFLOW_ORDER_CLASSIFY_RECEIVED_RECEPTION_CLOSED') && ($action == 'RECEPTION_CLOSED'))
487 )
488 ) {
489 $qtyshipped = array();
490 $qtyordred = array();
491
492 // The original purchase order is id in $object->origin_id
493 // Find all reception on purchase order origin
494
495 if (in_array($object->origin, array('order_supplier', 'supplier_order', 'commandeFournisseur')) && $object->origin_id > 0) {
496 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
497 $order = new CommandeFournisseur($this->db);
498 $ret = $order->fetch($object->origin_id);
499 if ($ret < 0) {
500 $this->setErrorsFromObject($order);
501 return $ret;
502 }
503 $ret = $order->fetchObjectLinked($order->id, $order->element, null, 'reception');
504 if ($ret < 0) {
505 $this->setErrorsFromObject($order);
506 return $ret;
507 }
508
509 // Build array of quantity received by product for a purchase order
510 if (is_array($order->linkedObjects) && count($order->linkedObjects) > 0) {
511 foreach ($order->linkedObjects as $type => $shipping_array) {
512 if ($type != 'reception' || !is_array($shipping_array) || count($shipping_array) == 0) {
513 continue;
514 }
515
516 foreach ($shipping_array as $shipping) {
517 if (!is_array($shipping->lines) || count($shipping->lines) == 0) {
518 continue;
519 }
520
521 foreach ($shipping->lines as $shippingline) {
522 $qtyshipped[$shippingline->fk_product] += $shippingline->qty;
523 }
524 }
525 }
526 }
527
528 // Build array of quantity ordered to be received
529 if (is_array($order->lines) && count($order->lines) > 0) {
530 foreach ($order->lines as $orderline) {
531 // Exclude lines not qualified for shipment, similar code is found into calcAndSetStatusDispatch() for vendors
532 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $orderline->product_type > 0) {
533 continue;
534 }
535 $qtyordred[$orderline->fk_product] += $orderline->qty;
536 }
537 }
538 //dol_syslog(var_export($qtyordred,true),LOG_DEBUG);
539 //dol_syslog(var_export($qtyshipped,true),LOG_DEBUG);
540 //Compare array
541 $diff_array = array_diff_assoc($qtyordred, $qtyshipped);
542 if (count($diff_array) == 0) {
543 //No diff => mean everything is received
544 $ret = $order->setStatut(CommandeFournisseur::STATUS_RECEIVED_COMPLETELY, null, '', 'SUPPLIER_ORDER_CLOSE');
545 if ($ret < 0) {
546 $this->setErrorsFromObject($order);
547 return $ret;
548 }
549 }
550 }
551 }
552 }
553
554 if ($action == 'TICKET_CREATE') {
555 dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
556 // Auto link ticket to contract
557 if (isModEnabled('contract') && isModEnabled('ticket') && isModEnabled('workflow') && getDolGlobalString('WORKFLOW_TICKET_LINK_CONTRACT') && getDolGlobalString('TICKET_PRODUCT_CATEGORY') && !empty($object->fk_soc)) {
558 $societe = new Societe($this->db);
559 $company_ids = (!getDolGlobalString('WORKFLOW_TICKET_USE_PARENT_COMPANY_CONTRACTS')) ? [$object->fk_soc] : $societe->getParentsForCompany($object->fk_soc, [$object->fk_soc]);
560
561 $contrat = new Contrat($this->db);
562 $number_contracts_found = 0;
563 foreach ($company_ids as $company_id) {
564 $contrat->socid = $company_id;
565 $list = $contrat->getListOfContracts('all', array(Contrat::STATUS_DRAFT, Contrat::STATUS_VALIDATED), array(getDolGlobalString('TICKET_PRODUCT_CATEGORY')), array(ContratLigne::STATUS_INITIAL, ContratLigne::STATUS_OPEN));
566 if (!is_array($list) || empty($list)) {
567 continue;
568 }
569 $number_contracts_found = count($list);
570 if ($number_contracts_found == 0) {
571 continue;
572 }
573
574 foreach ($list as $linked_contract) {
575 $object->setContract($linked_contract->id);
576 // don't set '$contractid' so it is not used when creating an intervention.
577 }
578
579 if ($number_contracts_found > 1 && !defined('NOLOGIN')) {
580 setEventMessages($langs->trans('TicketManyContractsLinked'), null, 'warnings');
581 }
582 break;
583 }
584 if ($number_contracts_found == 0 && !defined('NOLOGIN')) {
585 setEventMessages($langs->trans('TicketNoContractFoundToLink'), null, 'mesgs');
586 }
587 }
588 // Automatically create intervention
589 if (isModEnabled('intervention') && isModEnabled('ticket') && isModEnabled('workflow') && getDolGlobalString('WORKFLOW_TICKET_CREATE_INTERVENTION')) {
590 $fichinter = new Fichinter($this->db);
591 $fichinter->socid = (int) $object->fk_soc;
592 $fichinter->fk_project = (int) $object->fk_project;
593 $fichinter->fk_contrat = (int) $object->fk_contract;
594
595 $fichinter->user_author_id = $user->id;
596 $fichinter->model_pdf = getDolGlobalString('FICHEINTER_ADDON_PDF', 'soleil');
597
598 $fichinter->origin = $object->element;
599 $fichinter->origin_type = $object->element;
600 $fichinter->origin_id = $object->id;
601
602 // Extrafields
603 $extrafields = new ExtraFields($this->db);
604 $extrafields->fetch_name_optionals_label($fichinter->table_element);
605 $array_options = $extrafields->getOptionalsFromPost($fichinter->table_element);
606 $fichinter->array_options = $array_options;
607
608 $id = $fichinter->create($user);
609 if ($id <= 0) {
610 setEventMessages($fichinter->error, null, 'errors');
611 }
612 }
613 }
614 return 0;
615 }
616
627 private function shouldClassify($conf, $totalonlinkedelements, $object_total_ht)
628 {
629 // if the configuration allows unmatching amounts, allow classification anyway
630 if (getDolGlobalString('WORKFLOW_CLASSIFY_IF_AMOUNTS_ARE_DIFFERENTS')) {
631 return true;
632 }
633 // if the amount are same, allow classification, else deny
634 return (price2num($totalonlinkedelements, 'MT') == price2num($object_total_ht, 'MT'));
635 }
636}
$id
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
Class to manage predefined suppliers products.
const STATUS_RECEIVED_PARTIALLY
Received partially.
const STATUS_RECEIVED_COMPLETELY
Received completely.
const STATUS_ORDERSENT
Order sent, shipment on process.
Class to manage customers orders.
const STATUS_SHIPMENTONPROCESS
Shipment on process.
const STATUS_CLOSED
Closed (Sent, billed or not)
const STATUS_VALIDATED
Validated status.
Class to stock current configuration.
Class that all triggers must inherit.
runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
Function called when a Dolibarr business event is done.
setErrorsFromObject(CommonObject $object)
setErrorsFromObject
const STATUS_CLOSED
Closed status -> parcel was received by customer / end of process prev status : validated or shipment...
const STATUS_VALIDATED
Validated status -> parcel is ready to be sent prev status : draft next status : closed or shipment_i...
Class to manage standard extra fields.
Class to manage invoices.
const STATUS_VALIDATED
Validated (need to be paid)
const STATUS_CLOSED
Classified paid.
shouldClassify($conf, $totalonlinkedelements, $object_total_ht)
Class to manage proposals.
const STATUS_SIGNED
Signed quote.
const STATUS_BILLED
Billed or processed quote.
Class to manage third parties objects (customers, suppliers, prospects...)
const STATUS_SIGNED
Signed quote.
const STATUS_CLOSE
Billed or closed/processed quote.
Class to manage translations.
Class to manage Dolibarr users.
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:152