dolibarr/htdocs/expedition/card.php
VIAL-GOUTEYRON Quentin ea54342891
NEW Drag and drop a file on the 23 remaining cards (#39473)
* FIX Drag and drop of a file reports a wrong error, or none at all

The error handler of dragAndDropFileUpload() had three defects that all end on
the user believing the file was attached when it was not.

 - The first assignment of the message was dead code, there was no return after
   the test on the http code 403, so a refusal was reported as a generic error.
 - The key ErrorUploadPermissionDenied it used exists in no language file, so
   the raw key was shown.
 - JSON.parse() was called with no try/catch on an answer that is not always a
   json, a fatal error of the endpoint or a request over post_max_size for
   example. The exception left the user on a page with no message at all.
 - An empty list of files was treated as a success, while it means the endpoint
   stored nothing.

The value of PHP_SELF is also escaped before it is written into the 6 generated
javascript strings. It holds the path info of the request on a server that
accepts it, so it is a user input. dol_escape_js() is called with the mode that
escapes a double quote by a double quote, the strings being delimited by double
quotes, and the sequence '</' is escaped too because the function does not do it
and the path info could otherwise close the script tag and open one of its own.

Adds the key ErrorOnAtLeastOneFileUpload to en_US, used when some files of a
batch failed and some did not.

* FIX getMultidirOutput returns a directory the Documents tab does not read

Four defects of the same function, all ending on a file stored where the user
will never see it, or written outside the documents directory.

 - The ref of the project of a task was only passed through dol_sanitizePathName(),
   which keeps a slash, a colon and the accented chars, while projet/tasks/document.php
   sanitizes it with dol_sanitizeFileName(). A project ref holding a slash even
   created an extra level of directory. Measured on real databases: 304 projects
   over 330 hold a slash on one of them, carrying 701 tasks over 824.
 - The same case calls $object->fetchProject() with no guard, while the signature
   of the function accepts an object that is not a CommonObject, and even a null
   when a module is given. Such a caller gets a fatal error where it expects the
   error string. No core caller is in that case today, an external module or a
   hook can be.
 - The entity of the object may have no declared directory, an object shared by
   another entity for example. The undefined index returned a relative path, so
   the caller read or wrote under the web root. The current entity is used
   instead, and the fallback is logged because the directory is then not the one
   of the entity of the object, which matters for a caller that deletes files.
 - When the current entity has no declared directory either, the fallback
   returned the sub directory alone, again a relative path. The same error than
   for a module that declares no directory at all is now returned.

Adds the sub directory of a partnership and of a stock transfer, which their own
document tabs already read.

* FIX getElementProperties answers wrong properties for 9 elements

A customer payment, a supplier payment, a various payment, a stock transfer and
the 4 objects of the hrm module had no properties at all, or wrong ones, so any
caller that resolves a class, a table or a document directory from an element
failed on them.

 - payment, payment_supplier and payment_various had no branch. The branch of a
   customer payment tests $elementType and not $element, because the rule on the
   elements named myobject_mysubobject rewrites $element to 'payment' for
   'payment_salary' too, which is stored somewhere else.
 - job, position, skill and evaluation answered a wrong table (hrm_job_user for
   a position, and so on) and no sub directory, while their document tabs read
   one named after the element.
 - stocktransfer answered an empty classname, because it is not the ucfirst() of
   the element, so a caller doing new $classname($db) ended on a fatal error.
 - The sub directory was concatenated even when the module is disabled and the
   directory is empty, which answered a path at the root of the file system.
 - A contact and a conference are stored into a sub directory their tab reads.

isModEnabled('invoice') is tested for a customer payment: there is no module
named 'compta', so testing it was always false, while $conf->compta->payment is
set unconditionally by Conf::setValues() and could not be used as a proxy.

* FIX Access refused to everyone on 11 objects of the core

restrictedArea() and checkUserAccessToObject() refuse the access to objects that
no permission and no rule can match, whatever the user, an administrator
included.

 - The hrm module declares no permission at its first level, only 'all', and the
   stocktransfer module only 'stocktransfer'. A check on the module itself
   therefore tests a permission that does not exist. The mapping is the same one
   as into User::hasRight().
 - The module of an event organization declares no permission at all, its whole
   permission block being commented out on purpose, and its cards check the
   parent project instead. The feature is mapped onto that project, with the two
   guards the card has: an external user is refused, and so is a conference with
   no parent project, whose id of 0 would otherwise grant an access with no check
   on the record at all.
 - The default rule of checkUserAccessToObject() builds its sql on the columns
   entity and fk_soc of the table. llx_asset, llx_paiement, llx_paiementfourn and
   llx_workstation_workstation have no fk_soc, and llx_hrm_job, llx_hrm_job_user
   and llx_hrm_skill have neither. The sql failed, so the access was refused to
   every user this rule applies to. These tables are now checked on their entity
   only, which is what the $check rule already does for the same class of tables,
   and the 3 tables of hrm can be checked on nothing at all. The rule is selected
   on the table and not on the element, because $object is an id and not an
   object for most of the callers, the cards of an asset and of a workstation
   included, which are broken today for any user without the permission to see
   all third parties.
 - An external user is refused explicitly on those tables: none of these objects
   is linked to a third party, so the default rule refused him through a link
   that does not exist, and the rules that replace it do not look at the third
   party of the user at all.

Measured on a vanilla instance with 5 profiles, an administrator, an internal
user with every right, one without the permission to see all third parties, one
that is not a sales representative of the third party of the object, and an
external user: the 11 objects go from refused to granted for the internal users
and stay refused for the external one, and the 26 other elements answer exactly
the same for the 5 profiles.

* FIX A file dropped on a card is lost, or reported as refused when it was stored

FileUpload stores the file into a directory that the "Attached files" tab of the
object never reads, so the user attaches a file that no screen will ever show,
and nothing is indexed in database to find it back. Measured on real databases:
216487 thirdparties over 216887 and 157852 products over 280319 are in that case
on the cards that already enable the drag and drop.

 - The directory of the object is now forged with get_exdir(), the way the tabs
   do: it always uses the id for a thirdparty, whose ref is a company name and is
   not unique, and it falls back on the id when the ref is empty. The sub
   directory of the module is read with getMultidirOutput(), which knows the
   elements that store their documents into one. That function does not return
   an empty string when it fails but a string starting with 'error-', so only an
   absolute path is accepted: writing into that string would create the files
   under the web root.
 - fetchObjectByElement() returns an object even when fetch() returned 0. The
   object was then not loaded, and the file was stored at the root of the
   directory of the module, out of any object. The constructor now throws, and
   the endpoint answers the error with the same json contract than a successful
   call so that the caller can show it, instead of a fatal error and an http 500.
 - An attachment of the same name was silently overwritten, dol_move_uploaded_file()
   being called with $allowoverwrite = 1 while the name was checked before the
   ref of the object was added as a prefix. The check is done again on the final
   name, and on the .noexe suffixed name too, which that function appends to an
   executable file.
 - An executable file was renamed with that .noexe suffix and then reported as an
   error, while it was correctly stored.
 - The endpoint called restrictedArea() with an empty feature when the element is
   unknown, and the loop of that function then takes no branch at all and grants
   the access with no check. It refuses before, with the same http code and the
   same message than a refusal, so that a user cannot tell an object that exists
   but is not allowed from an object that does not exist. The refusal of an
   external user on an object of another third party answered a message of its
   own, which allowed the same enumeration.

Comes with the tests of the path resolution, of the fallback of get_exdir(), of
the rejection of the error string of getMultidirOutput(), and of the file name
deduplication.

* FIX Remove the drop area from 13 cards where dropping a file is harmful

Two distinct groups, both of them removing a drop area that only produces a
result the user does not want.

7 pages have no "Attached files" tab at all, so a file dropped on them can be
reached by no screen: a fiscal year and its info page, an accounting model, a
webhook trigger history, an intracomm report, a bookcal calendar and its booking
list. Measured: 4 of them accepted the upload and wrote an orphan file, the
accounting model wrote it at the root of the directory of the module, out of any
object, and the 2 bookcal ones answered an http 500.

6 cards print their tabs inside their edit form, so the drop area covered that
form: a product, an expense report, a VAT payment, a social contribution, a loan
and a salary. Dropping a file there reloads the page, which discards what the
user is typing. A product and an expense report have a second call for the read
only view, so the drop area is only removed from the call of the edit branch;
the 4 others share a single call between both views, so the parameter is now
conditional.

* NEW Drag and drop a file on the 23 remaining cards

Every card of the core that owns an "Attached files" tab can now receive a file
by drag and drop, which was the case of 32 of them only. The 23 added here are
an asset, an event, a various payment, a customer payment, a contact, a donation,
a conference or booth, a shipment, a supplier payment, a leave request, an
evaluation, a job, a position, a skill, a knowledge record, a manufacturing
order, a partnership, a lot, a stock transfer, a task, a reception, a resource
and a workstation.

The tab bar of a leave request and of a resource is printed inside their edit
form, so the drop area is not enabled there: dropping a file reloads the page,
which would discard what the user is typing. The card of a supplier payment
prints its tab bar even when the object was not loaded, so the drop area is only
enabled when it is.

* FIX getMultidirOutput refuses an entity with no directory instead of falling back [skip-claudemd]

The previous revision of this PR fell back on the directory of the current
entity when the entity of the object had none, with a LOG_WARNING. On a
multicompany install that made a caller read, write and above all delete
files in the directory of another entity. Refuse instead: the function
already answers 'error-diroutput-not-defined-for-this-object' when the
module declares no directory at all, so the caller has one behaviour to
handle, not two.

The entity is cast to int, which is what the array index needs and what
silences the four PhanTypeMismatchDimFetchNullable this function reported.

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-08-19 02:46:21 +02:00

3932 lines
164 KiB
PHP

<?php
/* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2005-2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005 Simon TOSSER <simon@kornog-computing.com>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2013 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
* Copyright (C) 2014-2017 Francis Appels <francis.appels@yahoo.com>
* Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
* Copyright (C) 2016-2018 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2016 Yasser Carreón <yacasia@gmail.com>
* Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2020 Lenin Rivas <lenin@leninrivas.com>
* Copyright (C) 2022 Josep Lluís Amador <joseplluis@lliuretic.cat>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 Nick Fragoulis
* Copyright (C) 2025 Charlene Benke <charlene@patas-monkey.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/expedition/card.php
* \ingroup expedition
* \brief Card of a shipment
*/
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var ExtraFields $extrafields
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
require_once DOL_DOCUMENT_ROOT . '/core/lib/sendings.lib.php';
require_once DOL_DOCUMENT_ROOT . '/core/modules/expedition/modules_expedition.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
require_once DOL_DOCUMENT_ROOT . '/product/stock/class/productlot.class.php';
require_once DOL_DOCUMENT_ROOT . '/commande/class/commande.class.php';
if (isModEnabled("product") || isModEnabled("service")) {
require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
}
if (isModEnabled("propal")) {
require_once DOL_DOCUMENT_ROOT . '/comm/propal/class/propal.class.php';
}
if (isModEnabled('productbatch')) {
require_once DOL_DOCUMENT_ROOT . '/product/class/productbatch.class.php';
}
if (isModEnabled('project')) {
require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
}
// Load translation files required by the page
$langs->loadLangs(array("sendings", "companies", "bills", 'orders', 'stocks', 'other', 'propal', 'productbatch'));
if (isModEnabled('incoterm')) {
$langs->load('incoterm');
}
if (isModEnabled('productbatch')) {
$langs->load('productbatch');
}
$origin = GETPOST('origin', 'alpha'); // Example: commande, propal
$origin_id = GETPOSTINT('origin_id');
$id = GETPOSTINT('id');
if (empty($origin_id) && !empty($origin)) {
$origin_id = GETPOSTINT('origin_id'); // Id of order or propal
}
if (empty($origin_id) && !empty($origin)) {
$origin_id = GETPOSTINT('object_id'); // Id of order or propal
}
$socid = GETPOSTINT('socid');
$ref = GETPOST('ref', 'alpha');
$line_id = GETPOSTINT('lineid');
$facid = GETPOSTINT('facid');
$contactid = GETPOSTINT('contactid');
$projectid = GETPOSTINT('projectid');
$action = GETPOST('action', 'alpha');
$confirm = GETPOST('confirm', 'alpha');
$cancel = GETPOST('cancel', 'alpha');
$rank = (GETPOSTINT('rank') > 0) ? GETPOSTINT('rank') : -1;
$lineid = GETPOSTINT('lineid');
$backtopage = GETPOST('backtopage', 'alpha');
//PDF
$hidedetails = (GETPOSTINT('hidedetails') ? GETPOSTINT('hidedetails') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0));
$hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0));
$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
$object = new Expedition($db);
$objectorder = new Commande($db);
// fetch optionals attributes and labels
$extrafields->fetch_name_optionals_label($object->table_element);
$extrafields->fetch_name_optionals_label($object->table_element_line);
$extrafields->fetch_name_optionals_label($objectorder->table_element_line);
// Load object. Make an object->fetch
include DOL_DOCUMENT_ROOT . '/core/actions_fetchobject.inc.php'; // Must be 'include', not 'include_once'
if (empty($origin) && !empty($object->origin_type) && $object->origin_id > 0) {
$origin = $object->origin_type;
} elseif (empty($origin) && is_string($object->origin) && $object->origin !== '' && $object->origin_id > 0) {
$origin = $object->origin;
}
if (empty($origin_id) && $object->origin_id > 0) {
$origin_id = $object->origin_id;
}
$origin_type = is_string($origin) ? $origin : '';
// Permissions / Rights
$usercanread = $user->hasRight("expedition", "lire");
$usercancreate = $user->hasRight("expedition", "creer");
$usercandelete = $user->hasRight("expedition", "supprimer");
// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
$hookmanager->initHooks(array('expeditioncard', 'globalcard'));
$date_delivery = dol_mktime(GETPOSTINT('date_deliveryhour'), GETPOSTINT('date_deliverymin'), 0, GETPOSTINT('date_deliverymonth'), GETPOSTINT('date_deliveryday'), GETPOSTINT('date_deliveryyear'));
$date_shipping = dol_mktime(GETPOSTINT('date_shippinghour'), GETPOSTINT('date_shippingmin'), 0, GETPOSTINT('date_shippingmonth'), GETPOSTINT('date_shippingday'), GETPOSTINT('date_shippingyear'));
// Security check
if ($user->socid) {
$socid = $user->socid;
}
$result = restrictedArea($user, 'expedition', $object->id, '');
$permissiondellink = $user->hasRight('expedition', 'delivery', 'creer'); // Used by the include of actions_dellink.inc.php
$permissiontoadd = $user->hasRight('expedition', 'creer');
$permissiontoedit = $usercancreate; // Used by the include of actions_lineupdown.inc.php
$permissiontoeditextra = $permissiontoadd;
if (GETPOST('attribute', 'aZ09') && isset($extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')])) {
// For action 'update_extras', is there a specific permission set for the attribute to update
$permissiontoeditextra = dol_eval((string) $extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')]);
}
$upload_dir = $conf->expedition->dir_output . '/sending';
$editColspan = 0;
$objectsrc = null;
$typeobject = null;
$ref_customer = null;
$shipping_method_id = null;
$warehouse_id = null;
$note_public = null;
$note_private = null;
/*
* Actions
*/
$error = 0;
$parameters = array();
$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
if ($reshook < 0) {
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
}
if (empty($reshook)) {
$backurlforlist = DOL_URL_ROOT.'/expedition/list.php';
if (empty($backtopage) || ($cancel && empty($id))) {
if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
$backtopage = $backurlforlist;
} else {
$backtopage = DOL_URL_ROOT.'/expedition/card.php?id='.((!empty($id) && $id > 0) ? $id : '__ID__');
}
}
}
if ($cancel) {
if (!empty($backtopageforcancel)) {
header("Location: ".$backtopageforcancel);
exit;
} elseif (!empty($backtopage)) {
header("Location: ".$backtopage);
exit;
}
$action = '';
}
if ($cancel) {
if ($origin_id > 0) {
if ($origin == 'commande') {
header("Location: " . DOL_URL_ROOT . '/expedition/shipment.php?id=' . ((int) $origin_id));
exit;
}
} else {
$action = '';
$object->fetch($object->id); // show shipment also after canceling modification
}
}
include DOL_DOCUMENT_ROOT . '/core/actions_dellink.inc.php'; // Must be 'include', not 'include_once'
// Actions to build doc
include DOL_DOCUMENT_ROOT . '/core/actions_builddoc.inc.php';
// Back to draft
if ($action == 'setdraft' && $permissiontoadd) {
$object->fetch($id);
$result = $object->setDraft($user, 0);
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
} else {
header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
exit;
}
}
// Reopen
if ($action == 'reopen' && $permissiontoadd) {
$object->fetch($id);
$result = $object->reOpen();
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
} else {
header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
exit;
}
}
// Set incoterm
if ($action == 'set_incoterms' && isModEnabled('incoterm') && $permissiontoadd) {
$result = $object->setIncoterms(GETPOSTINT('incoterm_id'), GETPOST('location_incoterms'));
}
if ($action == 'setref_customer' && $permissiontoadd) {
$result = $object->fetch($id);
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
}
$result = $object->setValueFrom('ref_customer', GETPOST('ref_customer', 'alpha'), '', null, 'text', '', $user, 'SHIPPING_MODIFY');
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
$action = 'editref_customer';
} else {
header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
exit;
}
}
if ($action == 'update_extras' && $permissiontoeditextra) {
$object->oldcopy = dol_clone($object, 2);
$attribute_name = GETPOST('attribute', 'aZ09');
// Fill array 'array_options' with data from update form
$ret = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
if ($ret < 0) {
$error++;
}
if (!$error) {
// Actions on extra fields
$result = $object->updateExtraField($attribute_name, 'SHIPPING_MODIFY');
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
}
}
if ($error) {
$action = 'edit_extras';
}
}
// Create shipment
if ($action == 'add' && $permissiontoadd) {
$db->begin();
if ($origin && $origin_id > 0) {
// We will loop on each line of the original document to complete the shipping object with various info and quantity to deliver
$classname = ucfirst($origin);
$objectsrc = new $classname($db);
'@phan-var-force Facture|Commande $objectsrc';
$objectsrc->fetch($origin_id);
$object->socid = $objectsrc->socid;
}
if (GETPOSTINT('socid') < 1 && $object->socid < 1) {
$error++;
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ThirdParty")), null, 'errors');
$action = 'create';
}
if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
$object->socid = GETPOSTINT('socid');
$object->fetch_thirdparty();
$object->origin = $origin; // deprecated
$object->origin_type = $origin_type;
$object->origin_id = $origin_id;
$object->fk_project = GETPOSTINT('projectid');
$object->weight = GETPOST('weight') == '' ? '' : GETPOSTFLOAT('weight');
$object->sizeH = GETPOST('sizeH') == '' ? '' : GETPOSTFLOAT('sizeH');
$object->sizeW = GETPOST('sizeW') == '' ? '' : GETPOSTFLOAT('sizeW');
$object->sizeS = GETPOST('sizeS') == '' ? '' : GETPOSTFLOAT('sizeS');
$object->size_units = GETPOSTINT('size_units');
$object->weight_units = GETPOSTINT('weight_units');
$object->ref_customer = GETPOST('ref_customer', 'alpha');
$object->model_pdf = GETPOST('model');
$object->date_delivery = $date_delivery; // Date delivery planned
$object->date_shipping = $date_shipping; // Sending date
$object->shipping_method_id = GETPOSTINT('shipping_method_id');
$object->tracking_number = GETPOST('tracking_number', 'alpha');
$object->note = GETPOST('note_private', 'restricthtml'); // deprecated
$object->note_private = GETPOST('note_private', 'restricthtml');
$object->note_public = GETPOST('note_public', 'restricthtml');
$object->fk_incoterms = GETPOSTINT('incoterm_id');
$object->location_incoterms = GETPOST('location_incoterms', 'alpha');
$product = new Product($db);
// Fill array 'array_options' with data from add form
$ret = $extrafields->setOptionalsFromPost(null, $object);
if ($ret < 0) {
$error++;
}
if (!$error) {
$ret = $object->create($user);
if ($ret <= 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
}
}
}
if ($origin && $origin_id > 0) {
$object->origin = $origin; // deprecated
$object->origin_type = $origin_type;
$object->origin_id = $origin_id;
$object->fk_project = GETPOSTINT('projectid');
$object->weight = GETPOST('weight') == '' ? '' : GETPOSTFLOAT('weight');
$object->sizeH = GETPOST('sizeH') == '' ? '' : GETPOSTFLOAT('sizeH');
$object->sizeW = GETPOST('sizeW') == '' ? '' : GETPOSTFLOAT('sizeW');
$object->sizeS = GETPOST('sizeS') == '' ? '' : GETPOSTFLOAT('sizeS');
$object->size_units = GETPOSTINT('size_units');
$object->weight_units = GETPOSTINT('weight_units');
$object->ref_customer = GETPOST('ref_customer', 'alpha');
$object->model_pdf = GETPOST('model');
$object->date_delivery = $date_delivery; // Date delivery planned
$object->date_shipping = $date_shipping; // Sending date
$object->shipping_method_id = GETPOSTINT('shipping_method_id');
$object->tracking_number = GETPOST('tracking_number', 'alpha');
$object->note = GETPOST('note_private', 'restricthtml'); // deprecated
$object->note_private = GETPOST('note_private', 'restricthtml');
$object->note_public = GETPOST('note_public', 'restricthtml');
$object->fk_incoterms = GETPOSTINT('incoterm_id');
$object->location_incoterms = GETPOST('location_incoterms', 'alpha');
// We will loop on each line of the original document to complete the shipping object with various info and quantity to deliver
$classname = ucfirst($object->origin_type);
$objectsrc = new $classname($db);
'@phan-var-force Facture|Commande $objectsrc';
$objectsrc->fetch($object->origin_id);
$object->socid = $objectsrc->socid;
$object->fk_delivery_address = $objectsrc->fk_delivery_address;
$product = new Product($db);
$batch_line = array();
$stockLine = array();
$array_options = array();
$num = count($objectsrc->lines);
$totalqty = 0;
$product_batch_used = array();
for ($i = 0; $i < $num; $i++) {
$idl = "idl" . $i;
$sub_qty = array();
$subtotalqty = 0;
$j = 0;
$batch = "batchl" . $i . "_0";
$stockLocation = "ent1" . $i . "_0";
$qty = "qtyl" . $i;
$is_batch_or_serial = 0;
if (!empty($objectsrc->lines[$i]->fk_product)) {
$resultFetch = $product->fetch($objectsrc->lines[$i]->fk_product, '', '', '', 1, 1, 1);
if ($resultFetch < 0) {
setEventMessages($product->error, $product->errors, 'errors');
}
$is_batch_or_serial = $product->status_batch;
}
// If product need a batch or serial number
if (isModEnabled('productbatch') && $objectsrc->lines[$i]->product_tobatch) {
if (GETPOSTISSET($batch)) {
//shipment line with batch-enable product
$qty .= '_' . $j;
while (GETPOSTISSET($batch)) {
// save line of detail into sub_qty
$sub_qty[$j]['q'] = price2num(GETPOST($qty, 'alpha'), 'MS'); // the qty we want to move for this stock record
$sub_qty[$j]['id_batch'] = GETPOSTINT($batch); // the id into llx_product_batch of stock record to move
$subtotalqty += $sub_qty[$j]['q'];
if ($is_batch_or_serial == 2 && ($sub_qty[$j]['q'] > 1 || ($sub_qty[$j]['q'] > 0 && in_array($sub_qty[$j]['id_batch'], $product_batch_used)))) {
setEventMessages($langs->trans("TooManyQtyForSerialNumber", $product->ref, ''), null, 'errors');
$totalqty = 0;
break 2;
}
if ($is_batch_or_serial == 2 && $sub_qty[$j]['q'] > 0) {
// we stock the batch id to test later if the same serial is shipped on another line for the same product
$product_batch_used[$j] = $sub_qty[$j]['id_batch'];
}
$j++;
$batch = "batchl" . $i . "_" . $j;
$qty = "qtyl" . $i . '_' . $j;
}
$batch_line[$i]['detail'] = $sub_qty; // array of details
$batch_line[$i]['qty'] = $subtotalqty;
$batch_line[$i]['ix_l'] = GETPOSTINT($idl);
$totalqty += $subtotalqty;
} else {
// No detail were provided for lots, so if a qty was provided, we can throw an error.
if (GETPOST($qty)) {
// We try to set an amount
// Case we don't use the list of available qty for each warehouse/lot
// GUI does not allow this yet
setEventMessages($langs->trans("StockIsRequiredToChooseWhichLotToUse") . ' (' . $langs->trans("Line") . ' ' . GETPOSTINT($idl) . ')', null, 'errors');
$error++;
}
}
} elseif (GETPOSTISSET($stockLocation)) {
//shipment line from multiple stock locations
$qty .= '_' . $j;
while (GETPOSTISSET($stockLocation)) {
// save sub line of warehouse
$stockLine[$i][$j]['qty'] = price2num(GETPOST($qty, 'alpha'), 'MS');
$stockLine[$i][$j]['warehouse_id'] = GETPOSTINT($stockLocation);
$stockLine[$i][$j]['ix_l'] = GETPOSTINT($idl);
$totalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
$subtotalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
$j++;
$stockLocation = "ent1" . $i . "_" . $j;
$qty = "qtyl" . $i . '_' . $j;
}
} else {
//shipment line for product with no batch management and no multiple stock location
if (GETPOSTFLOAT($qty) > 0) {
$totalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
$subtotalqty = price2num(GETPOST($qty, 'alpha'), 'MS');
}
}
// check qty shipped not greater than ordered
if (getDolGlobalInt("MAIN_DONT_SHIP_MORE_THAN_ORDERED") && $subtotalqty > $objectsrc->lines[$i]->qty) {
setEventMessages($langs->trans("ErrorTooMuchShipped", $i + 1), null, 'errors');
$error++;
continue;
}
// Extrafields
$array_options[$i] = $extrafields->getOptionalsFromPost($object->table_element_line, (string) $i);
// Unset extrafield
if (isset($extrafields->attributes[$object->table_element_line]['label']) && is_array($extrafields->attributes[$object->table_element_line]['label'])) {
// Get extra fields
foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
unset($_POST["options_" . $key]);
}
}
}
if (($totalqty > 0 || getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) && !$error) { // There is at least one thing to ship and no error
$selected_subtotal_lines = GETPOST('subtotal_toselect', 'array:int');
for ($i = 0; $i < $num; $i++) {
$qty = "qtyl" . $i;
if (!isset($batch_line[$i])) {
// not batch mode
if (isset($stockLine[$i])) {
//shipment from multiple stock locations
$nbstockline = count($stockLine[$i]);
for ($j = 0; $j < $nbstockline; $j++) {
if ($stockLine[$i][$j]['qty'] > 0 || ($stockLine[$i][$j]['qty'] == 0 && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS'))) {
$ret = $object->addline($stockLine[$i][$j]['warehouse_id'], $stockLine[$i][$j]['ix_l'], (float) $stockLine[$i][$j]['qty'], $array_options[$i]);
if ($ret < 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
}
}
}
} else {
if (GETPOSTFLOAT($qty) > 0 || getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
$ent = "entl" . $i;
$idl = "idl" . $i;
$entrepot_id = is_numeric(GETPOSTINT($ent)) ? GETPOSTINT($ent) : GETPOSTINT('entrepot_id');
if ($entrepot_id < 0) {
$entrepot_id = '';
}
if (!($objectsrc->lines[$i]->fk_product > 0)) {
$entrepot_id = 0;
}
$ret = $object->addline($entrepot_id, GETPOSTINT($idl), (float) price2num(GETPOSTFLOAT($qty), 'MS'), $array_options[$i]);
if ($ret < 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
}
}
if (isModEnabled('subtotals') && $objectsrc->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && in_array($objectsrc->lines[$i]->id, $selected_subtotal_lines)) {
$object->addSubtotalLine($langs, $objectsrc->lines[$i]->desc, (int) $objectsrc->lines[$i]->qty, $objectsrc->lines[$i]->extraparams, $objectsrc->lines[$i]->id);
}
}
} else {
// batch mode
if ($batch_line[$i]['qty'] > 0 || ($batch_line[$i]['qty'] == 0 && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS'))) {
$origin_line_id = (int) $batch_line[$i]['ix_l'];
$origin_line = new OrderLine($db);
$res = $origin_line->fetch($origin_line_id);
if ($res <= 0) {
$error++;
setEventMessages($origin_line->error, $origin_line->errors, 'errors');
}
$ret = $object->addline_batch($batch_line[$i], $array_options[$i], $origin_line);
if ($ret < 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
}
}
}
}
// Fill array 'array_options' with data from add form
$ret = $extrafields->setOptionalsFromPost(null, $object);
if ($ret < 0) {
$error++;
}
if (!$error) {
$ret = $object->create($user); // This create shipment (like Odoo picking) and lines of shipments. Stock movement will be done when validating or closing shipment.
if ($ret <= 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
}
}
} elseif (!$error) {
$labelfieldmissing = $langs->transnoentitiesnoconv("QtyToShip");
if (isModEnabled('stock')) {
$labelfieldmissing .= '/' . $langs->transnoentitiesnoconv("Warehouse");
}
setEventMessages($langs->trans("ErrorFieldRequired", $labelfieldmissing), null, 'errors');
$error++;
}
}
if (!$error) {
$db->commit();
$object->fetch_lines();
foreach ($object->lines as $line) {
$objectsrc_line = new $objectsrc->class_element_line($db);
'@phan-var-force CommonObjectLine $objectsrc_line';
$objectsrc_line->fetch($line->origin_line_id);
$line->extraparams = $objectsrc_line->extraparams;
$line->setExtraParameters();
}
header("Location: card.php?id=" . $object->id);
exit;
} else {
$db->rollback();
//$_GET["commande_id"] = GETPOSTINT('commande_id');
$action = 'create';
}
} elseif ($action == 'create_delivery' && getDolGlobalInt('MAIN_SUBMODULE_DELIVERY') && $user->hasRight('expedition', 'delivery', 'creer')) {
// Build a receiving receipt
$db->begin();
$result = $object->create_delivery($user);
if ($result > 0) {
$db->commit();
header("Location: " . DOL_URL_ROOT . '/delivery/card.php?action=create_delivery&token=' . newToken() . '&id=' . $result);
exit;
} else {
$db->rollback();
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif (
$action == 'confirm_valid' && $confirm == 'yes' && ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'creer'))
|| (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'shipping_advance', 'validate')))
) {
$object->fetch_thirdparty();
$result = $object->valid($user);
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
} else {
// Define output language
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
$outputlangs = $langs;
$newlang = '';
if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
$model = $object->model_pdf;
$ret = $object->fetch($id); // Reload to get new records
$result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
if ($result < 0) {
dol_print_error($db, $object->error, $object->errors);
}
}
}
} elseif ($action == 'confirm_cancel' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
$also_update_stock = (GETPOST('alsoUpdateStock', 'alpha') ? 1 : 0);
$result = $object->cancel($user, 0, (bool) $also_update_stock);
if ($result > 0) {
$result = $object->setStatut(Expedition::STATUS_CANCELED);
} else {
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif ($action == 'confirm_delete' && $confirm == 'yes' && $user->hasRight('expedition', 'supprimer')) {
$also_update_stock = (GETPOST('alsoUpdateStock', 'alpha') ? 1 : 0);
$result = $object->delete($user, 0, (bool) $also_update_stock);
if ($result > 0) {
header("Location: " . DOL_URL_ROOT . '/expedition/index.php');
exit;
} else {
setEventMessages($object->error, $object->errors, 'errors');
}
// TODO add alternative status
//} elseif ($action == 'reopen' && ($user->hasRight('expedition', 'creer') || $user->hasRight('expedition', 'shipping_advance', 'validate')))
//{
// $result = $object->setStatut(Expedition::STATUS_DRAFT);
// if ($result < 0)
// {
// setEventMessages($object->error, $object->errors, 'errors');
// }
//}
} elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $permissiontoadd) {
$result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline'), $user);
if ($result > 0) {
// reorder lines
$object->line_order(true, 'ASC', false);
// Define output language
$outputlangs = $langs;
$newlang = '';
if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
$ret = $object->fetch($object->id); // Reload to get new records
$object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
}
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit;
} else {
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif ($action == 'confirm_deleteline' && $confirm == 'yes' && $usercancreate) {
// Remove a product line
$result = $object->deleteLine($user, $lineid);
if ($result > 0) {
// reorder lines
$object->line_order(true);
// Define output language
$outputlangs = $langs;
$newlang = '';
if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
$ret = $object->fetch($object->id); // Reload to get new records
$object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
}
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit;
} else {
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif ($action == 'confirm_sign' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
$result = $object->setSignedStatus($user, GETPOSTINT('signed_status'), 0, 'SHIPPING_MODIFY');
if ($result >= 0) {
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit;
} else {
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif ($action == 'confirm_unsign' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
$result = $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_NO_SIGNATURE'], 0, 'SHIPPING_MODIFY');
if ($result >= 0) {
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit;
} else {
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif ($action == 'setdate_livraison' && $user->hasRight('expedition', 'creer')) {
$datedelivery = dol_mktime(GETPOSTINT('liv_hour'), GETPOSTINT('liv_min'), 0, GETPOSTINT('liv_month'), GETPOSTINT('liv_day'), GETPOSTINT('liv_year'));
$object->fetch($id);
$result = $object->setDeliveryDate($user, $datedelivery); // Set the planned delivery date
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif ($action == 'setdate_shipping' && $user->hasRight('expedition', 'creer')) {
$dateshipping = dol_mktime(GETPOSTINT('ship_hour'), GETPOSTINT('ship_min'), 0, GETPOSTINT('ship_month'), GETPOSTINT('ship_day'), GETPOSTINT('ship_year'));
$object->fetch($id);
$result = $object->setShippingDate($user, $dateshipping);
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
}
} elseif (in_array($action, array('settracking_number', 'settracking_url', 'settrueWeight', 'settrueWidth', 'settrueHeight', 'settrueDepth', 'setshipping_method_id')) && $user->hasRight('expedition', 'creer')) {
// Action update
$error = 0;
if ($action == 'settracking_number') { // Test on permission not required
$object->tracking_number = trim(GETPOST('tracking_number', 'alpha'));
$object->tracking_number = preg_replace('/\s+/', '', $object->tracking_number);
}
if ($action == 'settracking_url') { // Test on permission not required
$object->tracking_url = trim(GETPOST('tracking_url', 'restricthtml'));
}
if ($action == 'settrueWeight') { // Test on permission not required
$object->trueWeight = GETPOSTFLOAT('trueWeight');
$object->weight_units = GETPOSTINT('weight_units');
}
if ($action == 'settrueWidth') { // Test on permission not required
$object->trueWidth = GETPOSTFLOAT('trueWidth');
}
if ($action == 'settrueHeight') { // Test on permission not required
$object->trueHeight = GETPOSTFLOAT('trueHeight');
$object->size_units = GETPOSTINT('size_units');
}
if ($action == 'settrueDepth') { // Test on permission not required
$object->trueDepth = GETPOSTFLOAT('trueDepth');
}
if ($action == 'setshipping_method_id') { // Test on permission not required
$object->shipping_method_id = GETPOSTINT('shipping_method_id');
}
if ($object->update($user) >= 0) {
header("Location: card.php?id=" . $object->id);
exit;
}
setEventMessages($object->error, $object->errors, 'errors');
$action = "";
} elseif ($action == 'classifybilled' && $permissiontoadd) {
$object->fetch($id);
$result = $object->setBilled();
if ($result >= 0) {
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit();
}
setEventMessages($object->error, $object->errors, 'errors');
} elseif ($action == 'classifyclosed' && $permissiontoadd) {
$object->fetch($id);
$result = $object->setClosed();
if ($result >= 0) {
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit();
}
setEventMessages($object->error, $object->errors, 'errors');
} elseif ($action == 'deleteline' && !empty($line_id) && $permissiontoadd) {
// delete a line
$error = 0;
$object->fetch($id);
$lines = $object->lines;
$line = new ExpeditionLigne($db);
$line->fk_expedition = $object->id;
$num_prod = count($lines);
for ($i = 0; $i < $num_prod; $i++) {
if ($lines[$i]->id == $line_id) {
// A standalone shipment (SHIPMENT_STANDALONE) loads its lines with fetch_lines_free(),
// which does not set details_entrepot, so count() must not be called on it blindly.
if (is_array($lines[$i]->details_entrepot) && count($lines[$i]->details_entrepot) > 1) {
// delete multi warehouse lines
foreach ($lines[$i]->details_entrepot as $details_entrepot) {
$line->id = $details_entrepot->line_id;
if (!$error && $line->delete($user) < 0) {
$error++;
}
}
} else {
// delete single warehouse line
$line->id = $line_id;
if (!$error && $line->delete($user) < 0) {
$error++;
}
}
}
unset($_POST["lineid"]);
}
if (!$error) {
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit();
} else {
setEventMessages($line->error, $line->errors, 'errors');
}
} elseif ($action == 'updateline' && $permissiontoadd && GETPOST('cancel', 'alpha') != $langs->trans("Cancel")) {
if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
// Update a line
// Clean parameters
if (!$object->fetch($id) > 0) {
dol_print_error($db);
}
$object->fetch_thirdparty();
$qty = GETPOST('qty', 'alpha');
$description = '';
$fk_parent = 0;
$element_type = 'shipping';
$fk_unit = '';
$fk_product = 0;
$rang = 0;
// Extrafields
$extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
$array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
// Unset extrafield
if (is_array($extralabelsline)) {
// Get extra fields
foreach ($extralabelsline as $key => $value) {
unset($_POST["options_" . $key]);
}
}
$shipline = new ExpeditionLigne($db);
$shipline->fetch(GETPOSTINT('lineid'));
if (!$error) {
$result = $object->updatelinefree(GETPOSTINT('lineid'), (float) $qty, $element_type, $fk_product, GETPOSTINT('units'), $rang, $description, $fk_parent, 0, $array_options);
if ($result >= 0) {
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
// Define output language
$outputlangs = $langs;
$newlang = '';
if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
$outputlangs->load('products');
}
$ret = $object->fetch($object->id); // Reload to get new records
$object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
}
unset($_POST['qty']);
unset($_POST['units']);
} else {
setEventMessages($object->error, $object->errors, 'errors');
}
}
} elseif ($origin && $origin_id > 0) {
// Update a line
// Clean parameters
$qty = 0;
$entrepot_id = 0;
$batch_id = 0;
$lines = $object->lines;
$num_prod = count($lines);
for ($i = 0; $i < $num_prod; $i++) {
if ($lines[$i]->id == $line_id) { // we have found line to update
$update_done = false;
$line = new ExpeditionLigne($db);
$line->fk_expedition = $object->id;
// Extrafields Lines
$line->array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
// Unset extrafield POST Data
if (is_array($extrafields->attributes[$object->table_element_line]['label'])) {
foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
unset($_POST["options_" . $key]);
}
}
$line->fk_product = $lines[$i]->fk_product;
if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
// line with lot
foreach ($lines[$i]->detail_batch as $detail_batch) {
$lotStock = new Productbatch($db);
$batch = "batchl" . $detail_batch->fk_expeditiondet . "_" . $detail_batch->fk_origin_stock;
$qty = "qtyl" . $detail_batch->fk_expeditiondet . '_' . $detail_batch->id;
$batch_id = GETPOSTINT($batch);
$batch_qty = GETPOSTFLOAT($qty);
if (!empty($batch_id)) {
if ($lotStock->fetch($batch_id) > 0 && $line->fetch($detail_batch->fk_expeditiondet) > 0) { // $line is ExpeditionLine
if ($lines[$i]->entrepot_id != 0) {
// allow update line entrepot_id if not multi warehouse shipping
$line->entrepot_id = $lotStock->warehouseid;
}
// detail_batch can be an object with keys, or an array of ExpeditionLineBatch
if (empty($line->detail_batch)) {
$line->detail_batch = new stdClass();
}
$line->detail_batch->fk_origin_stock = $batch_id;
$line->detail_batch->batch = $lotStock->batch;
$line->detail_batch->id = $detail_batch->id;
$line->detail_batch->entrepot_id = $lotStock->warehouseid;
$line->detail_batch->qty = $batch_qty;
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
} else {
$update_done = true;
}
} else {
setEventMessages($lotStock->error, $lotStock->errors, 'errors');
$error++;
}
}
unset($_POST[$batch]);
unset($_POST[$qty]);
}
// add new batch
$lotStock = new Productbatch($db);
$batch = "batchl" . $line_id . "_0";
$qty = "qtyl" . $line_id . "_0";
$batch_id = GETPOSTINT($batch);
$batch_qty = GETPOSTFLOAT($qty);
$lineIdToAddLot = 0;
if ($batch_qty > 0 && !empty($batch_id)) {
if ($lotStock->fetch($batch_id) > 0) {
// check if lotStock warehouse id is same as line warehouse id
if ($lines[$i]->entrepot_id > 0) {
// single warehouse shipment line
if ($lines[$i]->entrepot_id == $lotStock->warehouseid) {
$lineIdToAddLot = $line_id;
}
} elseif (count($lines[$i]->details_entrepot) > 1) {
// multi warehouse shipment lines
foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
if ($detail_entrepot->entrepot_id == $lotStock->warehouseid) {
$lineIdToAddLot = $detail_entrepot->line_id;
}
}
}
if ($lineIdToAddLot) {
// add lot to existing line
if ($line->fetch($lineIdToAddLot) > 0) {
$line->detail_batch->fk_origin_stock = $batch_id;
$line->detail_batch->batch = $lotStock->batch;
$line->detail_batch->entrepot_id = $lotStock->warehouseid;
$line->detail_batch->qty = $batch_qty;
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
} else {
$update_done = true;
}
} else {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
}
} else {
// create new line with new lot
$line->origin_line_id = $lines[$i]->origin_line_id;
$line->entrepot_id = $lotStock->warehouseid;
$line->detail_batch[0] = new ExpeditionLineBatch($db);
$line->detail_batch[0]->fk_origin_stock = $batch_id;
$line->detail_batch[0]->batch = $lotStock->batch;
$line->detail_batch[0]->entrepot_id = $lotStock->warehouseid;
$line->detail_batch[0]->qty = $batch_qty;
if ($object->create_line_batch($line, $line->array_options) < 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
} else {
$update_done = true;
}
}
} else {
setEventMessages($lotStock->error, $lotStock->errors, 'errors');
$error++;
}
}
} else {
if ($lines[$i]->fk_product > 0) {
// line without lot
if ($lines[$i]->entrepot_id == 0) {
// single warehouse shipment line or line in several warehouses context but with warehouse not defined
$stockLocation = "entl" . $line_id;
$qty = "qtyl" . $line_id;
$line->id = $line_id;
$line->entrepot_id = GETPOSTINT((string) $stockLocation);
$line->qty = GETPOSTFLOAT($qty);
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
} else {
$update_done = true;
}
unset($_POST[$stockLocation]);
unset($_POST[$qty]);
} elseif ($lines[$i]->entrepot_id > 0) {
// single warehouse shipment line
$stockLocation = "entl" . $line_id;
$qty = "qtyl" . $line_id;
$line->id = $line_id;
$line->entrepot_id = GETPOSTINT($stockLocation);
$line->qty = GETPOSTFLOAT($qty);
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
} else {
$update_done = true;
}
unset($_POST[$stockLocation]);
unset($_POST[$qty]);
} elseif (count($lines[$i]->details_entrepot) > 1) {
// multi warehouse shipment lines
foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
if (!$error) {
$stockLocation = "entl" . $detail_entrepot->line_id;
$qty = "qtyl" . $detail_entrepot->line_id;
$warehouse = GETPOSTINT($stockLocation);
if (!empty($warehouse)) {
$line->id = $detail_entrepot->line_id;
$line->entrepot_id = $warehouse;
$line->qty = GETPOSTFLOAT($qty);
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
} else {
$update_done = true;
}
}
unset($_POST[$stockLocation]);
unset($_POST[$qty]);
}
}
} elseif (!isModEnabled('stock') && !isModEnabled('productbatch')) { // both product batch and stock are not activated.
$qty = "qtyl" . $line_id;
$line->id = $line_id;
$line->qty = GETPOSTFLOAT($qty);
$line->entrepot_id = 0;
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
} else {
$update_done = true;
}
unset($_POST[$qty]);
}
} else {
// Product no predefined
$qty = "qtyl" . $line_id;
$line->id = $line_id;
$line->qty = GETPOSTFLOAT($qty);
$line->entrepot_id = 0;
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
} else {
$update_done = true;
}
unset($_POST[$qty]);
}
}
if (empty($update_done)) {
$line->id = $lines[$i]->id;
$resultInsertExtraFields = $line->insertExtraFields();
if ($resultInsertExtraFields < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
} elseif ($resultInsertExtraFields > 0) {
$update_done = true;
}
}
if (empty($update_done) && !$error) {
setEventMessages($langs->trans('NoShipmentLineChangeDetected'), null, 'warnings');
}
}
}
unset($_POST["lineid"]);
if (!$error) {
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
// Define output language
$outputlangs = $langs;
$newlang = '';
if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
$ret = $object->fetch($object->id); // Reload to get new records
$object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
}
} else {
header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To redisplay the form being edited
exit();
}
}
} elseif ($action == 'updateline' && $permissiontoadd && GETPOST('cancel', 'alpha') == $langs->trans("Cancel")) {
header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To redisplay the form being edited
exit();
} elseif ($action == 'addline' && !$origin && getDolGlobalString('SHIPMENT_STANDALONE') && $usercancreate) { // Add a new line
$langs->load('errors');
$error = 0;
$line_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
$predef = '';
$description = '';
$fk_parent = 0;
$fk_elementdet = '';
$element_type = 'shipping';
$fk_unit = '';
$idprod = 0;
$fk_product = 0;
$fk_entrepot = '';
$rang = '';
$prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
if ($prod_entry_mode == 'free') {
$idprod = 0;
} else {
$idprod = GETPOSTINT('idprod');
if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
$error++;
}
}
$qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS', 2);
// Extrafields
$extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
$array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
// Unset extrafield
if (is_array($extralabelsline)) {
// Get extra fields
foreach ($extralabelsline as $key => $value) {
unset($_POST["options_".$key]);
}
}
if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
$error++;
}
if ($qty == '') {
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
$error++;
}
if ($qty < 0) {
setEventMessages($langs->trans('FieldCannotBeNegative', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
$error++;
}
if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && empty($line_desc)) {
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
$error++;
}
if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
if ($combinations = GETPOST('combinations', 'array')) {
// Check if there is a product with the given combination
$prodcomb = new ProductCombination($db);
if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
$idprod = $res->fk_product_child;
$fk_product = $idprod; // Update $fk_product with the fetched child product
} else {
setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
$error++;
}
}
}
if (!$error && ($qty >= 0) && (!empty($line_desc) || (!empty($idprod) && $idprod > 0))) {
// Clean parameters
if (!empty($idprod) && $idprod > 0) {
$prod = new Product($db);
$prod->fetch($idprod);
$desc = $prod->label;
$description = $desc;
// Define output language
if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
$outputlangs = $langs;
$newlang = '';
if (GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
$description = (!empty($prod->multilangs[$outputlangs->defaultlang]["description"])) ? $prod->multilangs[$outputlangs->defaultlang]["description"] : $prod->description;
} else {
$description = $prod->description;
}
if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 0) {
$description = dol_concatdesc($desc, $line_desc, false, getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION') ? true : false);
} else {
$description = $line_desc;
}
// Add custom code and origin country into description
if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
$tmptxt = '(';
if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
$outputlangs = $langs;
$newlang = '';
if (GETPOST('lang_id', 'alpha')) {
$newlang = GETPOST('lang_id', 'alpha');
}
if (empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
$outputlangs->load('products');
}
if (!empty($prod->customcode)) {
$tmptxt .= $outputlangs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
}
if (!empty($prod->customcode) && !empty($prod->country_code)) {
$tmptxt .= ' - ';
}
if (!empty($prod->country_code)) {
$tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $outputlangs, 0);
}
} else {
if (!empty($prod->customcode)) {
$tmptxt .= $langs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
}
if (!empty($prod->customcode) && !empty($prod->country_code)) {
$tmptxt .= ' - ';
}
if (!empty($prod->country_code)) {
$tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $langs, 0);
}
}
$tmptxt .= ')';
$description = dol_concatdesc($desc, $tmptxt);
}
$type = $prod->type;
$fk_unit = $prod->fk_unit;
} else {
$label = (GETPOST('product_label') ? GETPOST('product_label') : '');
$desc = $line_desc;
$type = GETPOST('type');
$fk_unit = GETPOST('units', 'alpha');
$description = $desc;
$fk_elementdet = '';
}
$desc = dol_htmlcleanlastbr($desc);
// Insert line
$result = $object->addlinefree((float) $qty, $element_type, $idprod, $fk_unit, min($rank, count($object->lines) + 1), $description, $fk_parent, $array_options);
if ($result > 0) {
$ret = $object->fetch($object->id); // Reload to get new records
$object->fetch_thirdparty();
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
// Define output language
$outputlangs = $langs;
$newlang = GETPOST('lang_id', 'alpha');
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
$object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
}
} else {
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$object->id); // To redisplay the form being edited
exit();
}
}
} elseif ($action == 'confirm_sign' && $confirm == 'yes' && $permissiontoadd) {
$result = $object->setSignedStatus($user, GETPOSTINT('signed_status'), 0, 'SHIPPING_MODIFY');
if ($result >= 0) {
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
// Define output language
$outputlangs = $langs;
$newlang = '';
if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
$object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
}
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit;
} else {
$mesg = $object->error;
}
} elseif ($action == 'confirm_unsign' && $confirm == 'yes' && $permissiontoadd) {
$result = $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_NO_SIGNATURE'], 0, 'SHIPPING_MODIFY');
if ($result >= 0) {
if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
// Define output language
$outputlangs = $langs;
$newlang = '';
if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
$object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
}
header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
exit;
} else {
$mesg = $object->error;
}
}
include DOL_DOCUMENT_ROOT . '/core/actions_printing.inc.php';
// Actions to send emails
if (empty($id)) {
$id = $facid;
}
$triggersendname = 'SHIPPING_SENTBYMAIL';
$paramname = 'id';
$autocopy = 'MAIN_MAIL_AUTOCOPY_SHIPMENT_TO';
$mode = 'emailfromshipment';
$trackid = 'shi' . $object->id;
include DOL_DOCUMENT_ROOT . '/core/actions_sendmails.inc.php';
}
/*
* View
*/
$title = $object->ref . ' - ' . $langs->trans("Card");
if ($action == 'create') {
$title = $langs->trans("NewSending");
}
$help_url = 'EN:Module_Shipments|FR:Module_Expéditions|ES:M&oacute;dulo_Expediciones|DE:Modul_Lieferungen';
llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-expedition page-card');
if (empty($action)) {
$action = 'view';
}
$form = new Form($db);
$formfile = new FormFile($db);
$formproduct = new FormProduct($db);
if (isModEnabled('project')) {
$formproject = new FormProjets($db);
} else {
$formproject = null;
}
$product_static = new Product($db);
$shipment_static = new Expedition($db);
$warehousestatic = new Entrepot($db);
if (!$origin && $action == 'create' && !getDolGlobalString('SHIPMENT_STANDALONE')) {
print load_fiche_titre($langs->trans("CreateShipment"), '', 'dolly');
print '<br>' .$langs->trans("ShipmentCreationIsDoneFromOrder");
$action = '';
$id = '';
$ref = '';
}
// Mode creation.
if ($action == 'create' && $usercancreate) {
$expe = new Expedition($db);
print load_fiche_titre($langs->trans("NewSending"), '', 'dolly');
if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
$soc = new Societe($db);
if ($socid > 0) {
$res = $soc->fetch($socid);
}
$shipping_method_id = $soc->shipping_method_id;
$note_private = $object->getDefaultCreateValueFor('note_private');
$note_public = $object->getDefaultCreateValueFor('note_public');
print '<form name="crea_expedition" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
print '<input type="hidden" name="token" value="' . newToken() . '">';
print '<input type="hidden" name="action" value="add">';
print '<input type="hidden" name="changecompany" value="0">'; // will be set to 1 by javascript so we know post is done after a company change
print '<input type="hidden" name="originid" value="' . $id . '">';
print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
print dol_get_fiche_head([]);
print '<table class="border centpercent">';
// Thirdparty
print '<tr>';
print '<td class="fieldrequired">' . $langs->trans('ThirdParty') . '</td>';
if ($socid > 0) {
print '<td>';
print $soc->getNomUrl(1);
print '<input type="hidden" name="socid" value="' . $soc->id . '">';
print '</td>';
} else {
print '<td class="valuefieldcreate">';
print img_picto('', 'company', 'class="pictofixedwidth"').$form->select_company('', 'socid', '', 'SelectThirdParty', 1, 0, array(), 0, 'minwidth175 maxwidth500 widthcentpercentminusxx');
// reload page to retrieve customer information
if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
print '<script>
$(document).ready(function() {
$("#socid").change(function() {
console.log("We have changed the company - Reload page");
var socid = $(this).val();
// reload page
$("input[name=action]").val("create");
$("input[name=changecompany]").val("1");
$("form[name=crea_expedition]").submit();
});
});
</script>';
}
print ' <a href="'.DOL_URL_ROOT.'/societe/card.php?action=create&client=3&fournisseur=0&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=create').'"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddThirdParty").'"></span></a>';
print '</td>';
}
print '</tr>'."\n";
// Project
if (isModEnabled('project') && is_object($formproject)) {
$projectid = GETPOSTINT('projectid');
if (empty($projectid) && !empty($object->fk_project)) {
$projectid = (int) $object->fk_project;
}
$langs->load("projects");
print '<tr>';
print '<td>' . $langs->trans("Project") . '</td><td colspan="2">';
print img_picto('', 'project', 'class="pictofixedwidth"');
print $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 1, 0, 'widthcentpercentminusxx');
print ' <a class="paddingleft" href="' . DOL_URL_ROOT . '/projet/card.php?socid=' . $soc->id . '&action=create&status=1&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?action=create&socid=' . $soc->id) . '"><span class="fa fa-plus-circle valignmiddle"></span></a>';
print '</td>';
print '</tr>';
}
// Date sending
print '<tr><td>'.$langs->trans("DateShipping").'</td>';
print '<td colspan="3">';
print img_picto('', 'action', 'class="pictofixedwidth"');
$date_shipping = ($date_shipping ? $date_shipping : $object->date_shipping); // $date_shipping comes from GETPOST
print $form->selectDate($date_shipping ? $date_shipping : -1, 'date_shipping', 1, 1, 1);
print "</td>\n";
print '</tr>';
// Date delivery planned
print '<tr><td>'.$langs->trans("DateDeliveryPlanned").'</td>';
print '<td colspan="3">';
print img_picto('', 'action', 'class="pictofixedwidth"');
$date_delivery = ($date_delivery ? $date_delivery : $object->delivery_date); // $date_delivery comes from GETPOST
print $form->selectDate($date_delivery ? $date_delivery : -1, 'date_delivery', 1, 1, 1);
print "</td>\n";
print '</tr>';
// Weight
print '<tr><td>';
print $langs->trans("Weight");
print '</td><td colspan="3">';
print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
print '<input name="weight" size="4" value="' . GETPOST('weight') . '"> '; // Do not use GETPOSTINT here, we must accept '' also.
$text = $formproduct->selectMeasuringUnits("weight_units", "weight", GETPOST('weight_units'), 0, 2);
$htmltext = $langs->trans("KeepEmptyForAutoCalculation");
print $form->textwithpicto($text, $htmltext);
print '</td></tr>';
// Dim width x height x depth
print '<tr><td>';
print $langs->trans("Width") . ' x ' . $langs->trans("Height") . ' x ' . $langs->trans("Depth");
print ' </td><td colspan="3">';
print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
print '<input name="sizeW" size="4" value="' . GETPOST('sizeW') . '">';
print ' x <input name="sizeH" size="4" value="' . GETPOST('sizeH') . '">';
print ' x <input name="sizeS" size="4" value="' . GETPOST('sizeS') . '">';
print ' ';
$text = $formproduct->selectMeasuringUnits("size_units", "size", GETPOST('size_units'), 0, 2);
$htmltext = $langs->trans("KeepEmptyForAutoCalculation");
print $form->textwithpicto($text, $htmltext);
print '</td></tr>';
// Delivery method
print "<tr><td>".$langs->trans("DeliveryMethod")."</td>";
print '<td colspan="3">';
$expe->fetch_delivery_methods();
print img_picto('', 'dolly', 'class="pictofixedwidth"');
print $form->selectarray("shipping_method_id", $expe->meths, GETPOSTINT('shipping_method_id'), 1, 0, 0, "", 1, 0, 0, '', 'widthcentpercentminusxx');
if ($user->admin) {
print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
}
print "</td></tr>\n";
// Tracking number
print "<tr><td>".$langs->trans("TrackingNumber")."</td>";
print '<td colspan="3">';
print img_picto('', 'barcode', 'class="pictofixedwidth"');
print '<input name="tracking_number" size="20" value="'.GETPOST('tracking_number', 'alpha').'">';
print "</td></tr>\n";
// Other attributes
$parameters = array('objectsrc' => isset($objectsrc) ? $objectsrc : '', 'colspan' => ' colspan="3"', 'cols' => '3', 'socid' => $socid);
$reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $expe, $action); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;
if (empty($reshook)) {
// copy from order
if ($object->fetch_optionals() > 0) {
$expe->array_options = array_merge($expe->array_options, $object->array_options);
}
print $expe->showOptionals($extrafields, 'edit', $parameters);
}
// Incoterms
if (isModEnabled('incoterm')) {
print '<tr>';
print '<td><label for="incoterm_id">'.$form->textwithpicto($langs->trans("IncotermLabel"), $object->label_incoterms, 1).'</label></td>';
print '<td colspan="3" class="maxwidthonsmartphone">';
print img_picto('', 'incoterm', 'class="pictofixedwidth"');
print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''));
print '</td></tr>';
}
// Document model
include_once DOL_DOCUMENT_ROOT.'/core/modules/expedition/modules_expedition.php';
$list = ModelePdfExpedition::liste_modeles($db);
if (is_array($list) && count($list) > 1) {
print "<tr><td>".$langs->trans("DefaultModel")."</td>";
print '<td colspan="3">';
print img_picto('', 'pdf', 'class="pictofixedwidth"');
print $form->selectarray('model', $list, getDolGlobalString('EXPEDITION_ADDON_PDF'), 0, 0, 0, '', 0, 0, 0, '', 'widthcentpercentminusx');
print "</td></tr>\n";
}
// Note Public
$htmltext = '';
print '<tr>';
print '<td class="tdtop">';
print $form->textwithpicto($langs->trans('NotePublic'), $htmltext);
print '</td>';
print '<td valign="top" colspan="2">';
$doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
print $doleditor->Create(1);
// Note Private
if (!$user->socid) {
print '<tr>';
print '<td class="tdtop">';
print $form->textwithpicto($langs->trans('NotePrivate'), $htmltext);
print '</td>';
print '<td valign="top" colspan="2">';
$doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
print $doleditor->Create(1);
print "</td></tr>";
}
print "</table>\n";
print dol_get_fiche_end();
print $form->buttonsSaveCancel("CreateDraft");
print "</form>\n";
} elseif ($origin) {
$classname = ucfirst($origin);
$object = new $classname($db);
/* @var Commande|Facture $object */
'@phan-var-force Commande|Facture $object';
if ($object->fetch($origin_id)) { // This include the fetch_lines
$soc = new Societe($db);
$soc->fetch($object->socid);
$author = new User($db);
$author->fetch($object->user_author_id);
if (isModEnabled('stock')) {
$entrepot = new Entrepot($db);
}
print '<form action="' . $_SERVER["PHP_SELF"] . '" method="post">';
print '<input type="hidden" name="token" value="' . newToken() . '">';
print '<input type="hidden" name="action" value="add">';
print '<input type="hidden" name="origin" value="' . $origin . '">';
print '<input type="hidden" name="origin_id" value="' . $object->id . '">';
if (GETPOSTINT('entrepot_id')) {
print '<input type="hidden" name="entrepot_id" value="' . GETPOSTINT('entrepot_id') . '">';
}
print dol_get_fiche_head([]);
print '<table class="border centpercent">';
// Ref
print '<tr><td class="titlefieldcreate fieldrequired">';
if ($origin == 'commande' && isModEnabled('order')) {
print $langs->trans("RefOrder");
}
if ($origin == 'propal' && isModEnabled("propal")) {
print $langs->trans("RefProposal");
}
print '</td><td colspan="3">';
print $object->getNomUrl(1);
print '</td>';
print "</tr>\n";
// Ref client
print '<tr><td>';
if ($origin == 'commande') {
print $langs->trans('RefCustomerOrder');
} elseif ($origin == 'propal') {
print $langs->trans('RefCustomerOrder');
} else {
print $langs->trans('RefCustomer');
}
print '</td><td colspan="3">';
print '<input type="text" name="ref_customer" value="' . $object->ref_client . '" />';
print '</td>';
print '</tr>';
// Thirdparty
print '<tr><td class="titlefieldcreate fieldrequired">' . $langs->trans('Company') . '</td>';
print '<td colspan="3">' . $soc->getNomUrl(1) . '</td>';
print '<input type="hidden" name="socid" value="' . $soc->id . '">';
print '</tr>';
// Project
if (isModEnabled('project') && is_object($formproject)) {
$projectid = GETPOSTINT('projectid');
if (empty($projectid) && !empty($object->fk_project)) {
$projectid = (int) $object->fk_project;
}
if ($origin == 'project') {
$projectid = ($object->id ? $object->id : 0);
}
$langs->load("projects");
print '<tr>';
print '<td>' . $langs->trans("Project") . '</td><td colspan="2">';
print img_picto('', 'project', 'class="pictofixedwidth"');
print $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 1, 0, 'widthcentpercentminusxx');
print ' <a class="paddingleft" href="' . DOL_URL_ROOT . '/projet/card.php?socid=' . $soc->id . '&action=create&status=1&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?action=create&socid=' . $soc->id) . '"><span class="fa fa-plus-circle valignmiddle"></span></a>';
print '</td>';
print '</tr>';
}
// Date delivery planned
print '<tr><td>' . $langs->trans("DateDeliveryPlanned") . '</td>';
print '<td colspan="3">';
print img_picto('', 'action', 'class="pictofixedwidth"');
$date_delivery = ($date_delivery ? $date_delivery : $object->delivery_date); // $date_delivery comes from GETPOST
print $form->selectDate($date_delivery ? $date_delivery : -1, 'date_delivery', 1, 1, 1);
print "</td>\n";
print '</tr>';
// Date shipment (sending)
print '<tr><td>' . $langs->trans("DateShipping") . '</td>';
print '<td colspan="3">';
print img_picto('', 'action', 'class="pictofixedwidth"');
//$date_shipping = ($date_shipping ? $date_shipping : $object->date_shipping); // $date_shipping comes from GETPOST
print $form->selectDate($date_shipping ? $date_shipping : -1, 'date_shipping', 1, 1, 1);
print "</td>\n";
print '</tr>';
// Note Public
print '<tr><td>' . $langs->trans("NotePublic") . '</td>';
print '<td colspan="3">';
$doleditor = new DolEditor('note_public', $object->note_public, '', 60, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
print $doleditor->Create(1);
print "</td></tr>";
// Note Private
if ($object->note_private && !$user->socid) {
print '<tr><td>' . $langs->trans("NotePrivate") . '</td>';
print '<td colspan="3">';
$doleditor = new DolEditor('note_private', $object->note_private, '', 60, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
print $doleditor->Create(1);
print "</td></tr>";
}
// Weight
print '<tr><td>';
print $langs->trans("Weight");
print '</td><td colspan="3">';
print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
print '<input name="weight" size="4" value="' . dol_escape_htmltag(GETPOST('weight')) . '"> '; // Do not use GETPOSTINT here, we must accept '' also.
$text = $formproduct->selectMeasuringUnits("weight_units", "weight", (string) GETPOST('weight_units'), 0, 2);
$htmltext = $langs->trans("KeepEmptyForAutoCalculation");
print $form->textwithpicto($text, $htmltext);
print '</td></tr>';
// Dim Width x Height x Depth
print '<tr><td>';
print $langs->trans("Width") . ' x ' . $langs->trans("Height") . ' x ' . $langs->trans("Depth");
print ' </td><td colspan="3">';
print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
print '<input name="sizeW" size="4" value="' . GETPOST('sizeW') . '">';
print ' x <input name="sizeH" size="4" value="' . GETPOST('sizeH') . '">';
print ' x <input name="sizeS" size="4" value="' . GETPOST('sizeS') . '">';
print ' ';
$text = $formproduct->selectMeasuringUnits("size_units", "size", GETPOST('size_units'), 0, 2);
$htmltext = $langs->trans("KeepEmptyForAutoCalculation");
print $form->textwithpicto($text, $htmltext);
print '</td></tr>';
// Delivery method
print "<tr><td>" . $langs->trans("DeliveryMethod") . "</td>";
print '<td colspan="3">';
$expe->fetch_delivery_methods();
print img_picto('', 'dolly', 'class="pictofixedwidth"');
print $form->selectarray("shipping_method_id", $expe->meths, GETPOSTINT('shipping_method_id'), 1, 0, 0, "", 1, 0, 0, '', 'widthcentpercentminusxx');
if ($user->admin) {
print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
}
print "</td></tr>\n";
// Tracking number
print "<tr><td>" . $langs->trans("TrackingNumber") . "</td>";
print '<td colspan="3">';
print img_picto('', 'barcode', 'class="pictofixedwidth"');
print '<input name="tracking_number" size="20" value="' . GETPOST('tracking_number', 'alpha') . '">';
print "</td></tr>\n";
// Incoterms
if (isModEnabled('incoterm')) {
print '<tr>';
print '<td><label for="incoterm_id">' . $form->textwithpicto($langs->trans("IncotermLabel"), $object->label_incoterms, 1) . '</label></td>';
print '<td colspan="3" class="maxwidthonsmartphone">';
print img_picto('', 'incoterm', 'class="pictofixedwidth"');
print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''));
print '</td></tr>';
}
// Document model
include_once DOL_DOCUMENT_ROOT . '/core/modules/expedition/modules_expedition.php';
$list = ModelePdfExpedition::liste_modeles($db);
if (is_array($list) && count($list) > 1) {
print "<tr><td>" . $langs->trans("DefaultModel") . "</td>";
print '<td colspan="3">';
print img_picto('', 'pdf', 'class="pictofixedwidth"');
print $form->selectarray('model', $list, getDolGlobalString('EXPEDITION_ADDON_PDF'), 0, 0, 0, '', 0, 0, 0, '', 'widthcentpercentminusx');
print "</td></tr>\n";
}
// Other attributes. Fields from hook formObjectOptions and Extrafields.
// $objectsrc is Commande|Facture
$objectsav = $object; // Because Expedition is $expe and not $object that is wrongly a duplicate of $objectsrc.
$object = $expe;
// Propagate extrafieldsvalue from source object to shipment object
if (isset($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && !empty($extrafields->attributes[$object->table_element]['label'])) {
foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
if (array_key_exists('options_'.$key, $objectsav->array_options)) { // We take value from order only if extrafield has the same name/key.
$object->array_options['options_'.$key] = $objectsav->array_options['options_'.$key];
}
}
}
$parameters = array(
'objectsrc' => isset($objectsrc) ? $objectsrc : '',
'cols' => '3',
'socid' => $socid
);
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php';
$object = $objectsav;
print "</table>";
print dol_get_fiche_end();
// Shipment lines
$numAsked = count($object->lines);
print '<script type="text/javascript">' . "\n";
print 'jQuery(document).ready(function() {' . "\n";
print 'jQuery("#autofill").click(function() {';
$i = 0;
while ($i < $numAsked) {
print 'jQuery("#qtyl' . $i . '").val(jQuery("#qtyasked' . $i . '").val() - jQuery("#qtydelivered' . $i . '").val());' . "\n";
if (isModEnabled('productbatch')) {
print 'jQuery("#qtyl' . $i . '_' . $i . '").val(jQuery("#qtyasked' . $i . '").val() - jQuery("#qtydelivered' . $i . '").val());' . "\n";
}
$i++;
}
print 'return false; });' . "\n";
print 'jQuery("#autoreset").click(function() { console.log("Reset values to 0"); jQuery(".qtyl").val(0);' . "\n";
print 'return false; });' . "\n";
print '});' . "\n";
print '</script>' . "\n";
print '<br>';
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';
// Load shipments already done for same order
$object->loadExpeditions();
$alreadyQtyBatchSetted = $alreadyQtySetted = array();
$title_lines_to_disable = array();
if ($numAsked) {
if (isModEnabled('subtotals')) {
if (!(getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES') || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
$title_lines_to_disable = $object->getDisabledShippmentSubtotalLines();
}
foreach ($object->lines as $line) {
if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
$show_check_add_buttons = true;
break;
}
}
}
print '<tr class="liste_titre">';
print '<td>';
if (isset($show_check_add_buttons)) {
print $form->showCheckAddButtons('checkforselect');
}
print $langs->trans("Description");
print '</td>';
print '<td class="center">' . $langs->trans("QtyOrdered") . '</td>';
print '<td class="center">' . $langs->trans("QtyShipped") . '</td>';
print '<td class="center">' . $langs->trans("QtyToShip");
if (!isModEnabled('productbatch')) {
print '<br><a href="#" id="autofill" class="opacitymedium link cursor cursorpointer">' . img_picto($langs->trans("Autofill"), 'autofill', 'class="paddingrightonly"') . '</a>';
print ' / ';
} else {
print '<br>';
}
print '<span id="autoreset" class="opacitymedium link cursor cursorpointer">' . img_picto($langs->trans("Reset"), 'eraser') . '</span>';
print '</td>';
if (isModEnabled('stock')) {
if (!isModEnabled('productbatch')) {
print '<td class="left">' . $langs->trans("Warehouse") . ' (' . $langs->trans("Stock") . ')</td>';
} else {
print '<td class="left">' . $langs->trans("Warehouse") . ' / ' . $langs->trans("Batch") . ' (' . $langs->trans("Stock") . ')</td>';
}
}
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td class="left">' . $langs->trans('StockEntryDate') . '</td>';
}
print "</tr>\n";
}
$warehouse_id = GETPOSTINT('entrepot_id');
$warehousePicking = array();
// get all warehouse children for picking
if ($warehouse_id > 0) {
$warehousePicking[] = $warehouse_id;
$warehouseObj = new Entrepot($db);
$warehouseObj->get_children_warehouses($warehouse_id, $warehousePicking);
}
$indiceAsked = 0;
while ($indiceAsked < $numAsked) {
$product = new Product($db);
$line = $object->lines[$indiceAsked];
$parameters = array('i' => $indiceAsked, 'line' => $line, 'num' => $numAsked);
$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
if ($reshook < 0) {
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
}
if (empty($reshook) && $line->special_code != SUBTOTALS_SPECIAL_CODE) {
// Show product and description
$type = $line->product_type ? $line->product_type : $line->fk_product_type;
// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
if (!empty($line->date_start)) {
$type = 1;
}
if (!empty($line->date_end)) {
$type = 1;
}
print '<!-- line for order line ' . $line->id . ' -->' . "\n";
print '<tr class="oddeven" id="row-' . $line->id . '">' . "\n";
$qtyProdCom = $line->qty;
$productChildrenNb = 0;
// Product label
if ($line->fk_product > 0) { // If predefined product
$res = $product->fetch($line->fk_product);
if ($res < 0) {
dol_print_error($db, $product->error, $product->errors);
}
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
$productChildrenNb = $product->hasFatherOrChild(1);
}
if ($productChildrenNb > 0) {
$product->loadStockForVirtualProduct('warehouseopen', $qtyProdCom);
} else {
$product->load_stock('warehouseopen'); // Load all $product->stock_warehouse[idwarehouse]->detail_batch
}
//var_dump($product->stock_warehouse[1]);
print '<td>';
print '<a name="' . $line->id . '"></a>'; // Anchor to retor to the line
// Show product and description
$product_static->type = $line->fk_product_type;
$product_static->id = $line->fk_product;
$product_static->ref = $line->ref;
$product_static->status = $line->product_tosell;
$product_static->status_buy = $line->product_tobuy;
$product_static->status_batch = $line->product_tobatch;
$showdescinproductdesc = getDolGlobalString('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE');
$text = $product_static->getNomUrl(1);
$text .= ' - ' . (!empty($line->label) ? $line->label : $line->product_label);
$description = ($showdescinproductdesc ? '' : dol_htmlentitiesbr($line->desc));
$description .= empty($product->stockable_product) ? $langs->trans('StockDisabled') : $langs->trans('StockEnabled');
print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
// Show range
print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
// Add description in form
if ($showdescinproductdesc) {
print ($line->desc && $line->desc != $line->product_label) ? '<br>' . dol_htmlentitiesbr($line->desc) : '';
}
$expLine = new ExpeditionLigne($db);
$srcLine = new OrderLine($db);
$srcLine->id = $line->id;
$srcLine->fetch_optionals(); // Fetch source line extrafields so shipment line creation matches order UI.
$expLine->array_options = array_merge($expLine->array_options, $srcLine->array_options);
$lineExtrafieldsHtml = $expLine->showOptionals($extrafields, 'edit', array(), (string) $indiceAsked, '', '1', 'line');
if (!empty($lineExtrafieldsHtml)) {
print '<div style="padding-top: 10px" id="extrafield_lines_area_create_' . $indiceAsked . '" name="extrafield_lines_area_create_' . $indiceAsked . '">';
print $lineExtrafieldsHtml;
print '</div>';
}
print '</td>';
} else {
print "<td>";
if ($type == 1) {
$text = img_object($langs->trans('Service'), 'service');
} else {
$text = img_object($langs->trans('Product'), 'product');
}
if (!empty($line->label)) {
$text .= ' <strong>' . $line->label . '</strong>';
print $form->textwithtooltip($text, $line->desc, 3, 0, '', (string) $i);
} else {
print $text . ' ' . nl2br($line->desc);
}
// Show range
print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
$expLine = new ExpeditionLigne($db);
$srcLine = new OrderLine($db);
$srcLine->id = $line->id;
$srcLine->fetch_optionals(); // Fetch source line extrafields so shipment line creation matches order UI.
$expLine->array_options = array_merge($expLine->array_options, $srcLine->array_options);
$lineExtrafieldsHtml = $expLine->showOptionals($extrafields, 'edit', array(), (string) $indiceAsked, '', '1', 'line');
if (!empty($lineExtrafieldsHtml)) {
print '<div style="padding-top: 10px" id="extrafield_lines_area_create_' . $indiceAsked . '" name="extrafield_lines_area_create_' . $indiceAsked . '">';
print $lineExtrafieldsHtml;
print '</div>';
}
print "</td>\n";
}
// unit of order
$unit_order = '';
if (getDolGlobalString('PRODUCT_USE_UNITS')) {
$unit_order = measuringUnitString($line->fk_unit);
}
// Qty
print '<td class="center">' . $line->qty;
print '<input name="qtyasked' . $indiceAsked . '" id="qtyasked' . $indiceAsked . '" type="hidden" value="' . $line->qty . '">';
if ($line->qty) {
print ' ' . $unit_order . '</td>';
}
// Qty already shipped
print '<td class="center">';
$quantityDelivered = isset($object->expeditions[$line->id]) ? $object->expeditions[$line->id] : '';
print $quantityDelivered;
print '<input name="qtydelivered' . $indiceAsked . '" id="qtydelivered' . $indiceAsked . '" type="hidden" value="' . $quantityDelivered . '">';
if ($quantityDelivered) {
print ' ' . $unit_order . '</td>';
}
// Qty to ship
$quantityAsked = $line->qty;
if ($line->product_type == Product::TYPE_SERVICE && !getDolGlobalString('STOCK_SUPPORTS_SERVICES') && !getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
$quantityToBeDelivered = 0;
} else {
if (is_numeric($quantityDelivered)) {
$quantityToBeDelivered = $quantityAsked - $quantityDelivered;
} else {
$quantityToBeDelivered = $quantityAsked;
}
}
$warehouseObject = null;
if (count($warehousePicking) == 1 || !($line->fk_product > 0) || !isModEnabled('stock')) { // If warehouse was already selected or if product is not a predefined, we go into this part with no multiwarehouse selection
print '<!-- Case warehouse already known or product not a predefined product -->';
//ship from preselected location
$stock = + (isset($product->stock_warehouse[$warehouse_id]->real) ? $product->stock_warehouse[$warehouse_id]->real : 0); // Convert to number
if (($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) || $product->stockable_product == Product::DISABLED_STOCK) {
$deliverableQty = $quantityToBeDelivered;
} else {
$deliverableQty = min($quantityToBeDelivered, $stock);
}
if ($deliverableQty < 0) {
$deliverableQty = 0;
}
if (!isModEnabled('productbatch') || !$product->hasbatch()) {
// Quantity to send
print '<td class="center">';
if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES'))) {
if (GETPOSTISSET('qtyl'.$indiceAsked) && GETPOST('qtyl'.$indiceAsked) !== '') {
$deliverableQty = GETPOSTFLOAT('qtyl'.$indiceAsked, 'MS');
}
print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
$qtylValue = $deliverableQty;
if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
$qtylValue = '';
} elseif (is_numeric($qtylValue)) {
// Render as locale number so GETPOSTFLOAT() on submit reads it back via price2num option=2 without confusing '.' for a thousand separator in es_ES.
$qtylValue = price($qtylValue);
}
print '<input name="qtyl' . $indiceAsked . '" id="qtyl' . $indiceAsked . '" class="qtyl right" type="text" size="4" value="' . $qtylValue . '">';
} else {
if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
print '<input name="qtyl' . $indiceAsked . '" id="qtyl' . $indiceAsked . '" type="hidden" value="0">';
}
print $langs->trans("NA");
}
print '</td>';
// Stock
if (isModEnabled('stock')) {
print '<td class="left">';
if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) { // Type of product need stock change ?
// Show warehouse combo list
$ent = "entl" . $indiceAsked;
$idl = "idl" . $indiceAsked;
$tmpentrepot_id = is_numeric(GETPOST($ent)) ? GETPOSTINT($ent) : $warehouse_id;
if ($line->fk_product > 0) {
print '<!-- Show warehouse selection -->';
$stockMin = false;
if (getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
$stockMin = 0;
}
if ($productChildrenNb > 0) {
print $formproduct->selectWarehouses($tmpentrepot_id, 'entl' . $indiceAsked, '', 1, 0, 0, '', 0, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
} else {
if ($product->stockable_product == Product::ENABLED_STOCK) {
print $formproduct->selectWarehouses($tmpentrepot_id, 'entl' . $indiceAsked, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
} else {
print img_warning() . ' ' . $langs->trans('StockDisabled');
}
}
if ($tmpentrepot_id > 0 && $tmpentrepot_id == $warehouse_id) {
//print $stock.' '.$quantityToBeDelivered;
if ($stock < $quantityToBeDelivered) {
print ' ' . img_warning($langs->trans("StockTooLow")); // Stock too low for this $warehouse_id but you can change warehouse
}
}
}
} else {
print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span><input name="entl' . $indiceAsked . '" id="entl' . $indiceAsked . '" type="hidden" value="0">';
}
print '</td>';
}
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print "</tr>\n";
// Show subproducts of product
if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
$product->get_sousproduits_arbo();
$prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
if (count($prods_arbo) > 0) {
foreach ($prods_arbo as $key => $value) {
//print $value[0];
$img = '';
if ($value['stock'] < $value['stock_alert']) {
$img = img_warning($langs->trans("StockTooLow"));
}
print "<tr class=\"oddeven\"><td>&nbsp; &nbsp; &nbsp; ->
<a href=\"" . DOL_URL_ROOT . "/product/card.php?id=" . $value['id'] . "\">" . $value['fullpath'] . "
</a> (" . $value['nb'] . ")</td><td class=\"center\"> " . $value['nb_total'] . "</td><td>&nbsp;</td><td>&nbsp;</td>
<td class=\"center\">" . $value['stock'] . " " . $img . "</td>";
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print "</tr>";
}
}
}
} else {
// Product need lot
print '<td></td><td></td>';
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print '</tr>'; // end line and start a new one for lot/serial
print '<!-- Case product need lot -->';
$staticwarehouse = new Entrepot($db);
if ($warehouse_id > 0) {
$staticwarehouse->fetch($warehouse_id);
}
$subj = 0;
// Define nb of lines suggested for this order line
$nbofsuggested = 0;
if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) {
$nbofsuggested++;
}
}
print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) { // $dbatch is instance of Productbatch
//var_dump($dbatch);
$batchStock = +$dbatch->qty; // To get a numeric
$deliverableQty = min($quantityToBeDelivered, $batchStock);
// Now we will check if we have to reduce the deliverableQty by taking into account the qty already suggested in previous line
if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
$deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
} else {
if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
$alreadyQtyBatchSetted[$line->fk_product] = array();
}
if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
}
$deliverableQty = min($quantityToBeDelivered, $batchStock);
}
if ($deliverableQty < 0) {
$deliverableQty = 0;
}
$inputName = 'qtyl' . $indiceAsked . '_' . $subj;
if (GETPOSTISSET($inputName)) {
$deliverableQty = GETPOST($inputName, 'int');
}
$tooltipClass = $tooltipTitle = '';
if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
$tooltipClass = ' classfortooltip';
$tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines') . ' : ' . $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
} else {
$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0;
}
$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '>';
print '<td colspan="3" ></td><td class="center">';
$qtylValue = $deliverableQty;
if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
$qtylValue = '';
} elseif (is_numeric($qtylValue)) {
$qtylValue = price($qtylValue);
}
print '<input class="qtyl ' . $tooltipClass . ' right" title="' . $tooltipTitle . '" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '_' . $subj . '" type="text" size="4" value="' . $qtylValue . '">';
print '</td>';
print '<!-- Show details of lot -->';
print '<td class="left">';
print $staticwarehouse->getNomUrl(0) . ' / ';
print '<input name="batchl' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $dbatch->id . '">';
$detail = '';
$detail .= $langs->trans("Batch") . ': ' . $dbatch->batch;
if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
$detail .= ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
}
if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
$detail .= ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
}
$detail .= ' - ' . $langs->trans("Qty") . ': ' . $dbatch->qty;
$detail .= '<br>';
print $detail;
$quantityToBeDelivered -= $deliverableQty;
if ($quantityToBeDelivered < 0) {
$quantityToBeDelivered = 0;
}
$subj++;
print '</td>';
if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td>' . dol_print_date($dbatch->context['stock_entry_date'], 'day') . '</td>'; //StockEntrydate
}
print '</tr>';
}
} else {
print '<!-- Case there is no details of lot at all -->';
print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
print '<input class="qtyl right" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '_' . $subj . '" type="text" size="4" value="0" disabled="disabled"> ';
print '</td>';
print '<td class="left">';
print img_warning() . ' ' . $langs->trans("NoProductToShipFoundIntoStock", $staticwarehouse->label);
print '</td>';
if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print '</tr>';
}
}
} else {
// ship from multiple locations
if (!isModEnabled('productbatch') || !$product->hasbatch()) {
print '<!-- Case warehouse not already known and product does not need lot -->';
print '<td></td><td></td>';
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print '</tr>' . "\n"; // end line and start a new one for each warehouse
print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
$subj = 0;
// Define nb of lines suggested for this order line
$nbofsuggested = 0;
foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
$nbofsuggested++;
}
}
$tmpwarehouseObject = new Entrepot($db);
foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { // $stock_warehouse is product_stock
$var = $subj % 2;
if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
// if a warehouse was selected by user, picking is limited to this warehouse and his children
continue;
}
$tmpwarehouseObject->fetch($warehouse_id);
if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
$stock = +$stock_warehouse->real; // Convert it to number
$deliverableQty = min($quantityToBeDelivered, $stock);
$deliverableQty = max(0, $deliverableQty);
// Quantity to send
print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '>';
print '<td colspan="3" ></td><td class="center"><!-- qty to ship (no lot management for product line indiceAsked=' . $indiceAsked . ') -->';
if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
// For a virtual product (kit), $deliverableQty is already min($quantityToBeDelivered, $stock) computed above
// and the per-warehouse "stock" returned by loadStockForVirtualProduct() is $qtyWish, not a shared physical
// stock. The cross-line $alreadyQtySetted cap must not be applied for kits, otherwise the 2nd and next order
// lines of the same kit would be wrongly pre-filled with 0.
$tooltipClass = $tooltipTitle = '';
if ($productChildrenNb <= 0) {
if (isset($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
$deliverableQty = min($quantityToBeDelivered, $stock - $alreadyQtySetted[$line->fk_product][intval($warehouse_id)]);
} else {
if (!isset($alreadyQtySetted[$line->fk_product])) {
$alreadyQtySetted[$line->fk_product] = array();
}
$deliverableQty = min($quantityToBeDelivered, $stock);
}
if ($deliverableQty < 0) {
$deliverableQty = 0;
}
if (!empty($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
$tooltipClass = ' classfortooltip';
$tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
} else {
$alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = 0;
}
$alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = $deliverableQty + $alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
}
$inputName = 'qtyl'.$indiceAsked.'_'.$subj;
if (GETPOSTISSET($inputName)) {
$deliverableQty = GETPOSTINT($inputName);
}
$qtylValue = $deliverableQty;
if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
$qtylValue = '';
} elseif (is_numeric($qtylValue)) {
$qtylValue = price($qtylValue);
}
print '<input class="qtyl' . $tooltipClass . ' right" title="' . $tooltipTitle . '" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '" type="text" size="4" value="' . $qtylValue . '">';
print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_id . '">';
} else {
if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
print '<input name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '" type="hidden" value="0">';
}
print $langs->trans("NA");
}
print '</td>';
// Stock
print '<td class="left">';
if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
if ($product->stockable_product == Product::ENABLED_STOCK || $productChildrenNb > 0) {
print $tmpwarehouseObject->getNomUrl(0) . ' ';
if ($productChildrenNb <= 0) {
print '<!-- Show details of stock -->';
print '(' . $stock . ')';
}
} else {
print img_warning() . ' ' . $langs->trans('StockDisabled');
}
} else {
print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
}
print '</td>';
$quantityToBeDelivered -= $deliverableQty;
if ($quantityToBeDelivered < 0) {
$quantityToBeDelivered = 0;
}
$subj++;
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print "</tr>\n";
}
}
// Show subproducts of product (not recommended)
if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
$product->get_sousproduits_arbo();
$prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
if (count($prods_arbo) > 0) {
foreach ($prods_arbo as $key => $value) {
//print $value[0];
$img = '';
if ($value['stock'] < $value['stock_alert']) {
$img = img_warning($langs->trans("StockTooLow"));
}
print '<tr class"oddeven"><td>';
print "&nbsp; &nbsp; &nbsp; ->
<a href=\"" . DOL_URL_ROOT . "/product/card.php?id=" . $value['id'] . "\">" . $value['fullpath'] . "
</a> (" . $value['nb'] . ")</td><td class=\"center\"> " . $value['nb_total'] . "</td><td>&nbsp;</td><td>&nbsp;</td>
<td class=\"center\">" . $value['stock'] . " " . $img . "</td>";
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print "</tr>";
}
}
}
} else {
print '<!-- Case warehouse not already known and product need lot -->';
print '<td></td><td></td>';
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print '</tr>'; // end line and start a new one for lot/serial
$subj = 0;
print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
$tmpwarehouseObject = new Entrepot($db);
$productlotObject = new Productlot($db);
// Define nb of lines suggested for this order line
$nbofsuggested = 0;
foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
$nbofsuggested += count($stock_warehouse->detail_batch);
}
}
foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
$var = $subj % 2;
if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
// if a warehouse was selected by user, picking is limited to this warehouse and his children
continue;
}
$tmpwarehouseObject->fetch($warehouse_id);
if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
foreach ($stock_warehouse->detail_batch as $dbatch) {
$batchStock = +$dbatch->qty; // To get a numeric
if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
$deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
} else {
if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
$alreadyQtyBatchSetted[$line->fk_product] = array();
}
if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
}
$deliverableQty = min($quantityToBeDelivered, $batchStock);
}
if ($deliverableQty < 0) {
$deliverableQty = 0;
}
$inputName = 'qtyl' . $indiceAsked . '_' . $subj;
if (GETPOSTISSET($inputName)) {
$deliverableQty = GETPOSTINT($inputName);
}
$tooltipClass = $tooltipTitle = '';
if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
$tooltipClass = ' classfortooltip';
$tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines') . ' : ' . $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
} else {
$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0;
}
$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '><td colspan="3"></td><td class="center">';
$qtylValue = $deliverableQty;
if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
$qtylValue = '';
} elseif (is_numeric($qtylValue)) {
$qtylValue = price($qtylValue);
}
print '<input class="qtyl right ' . $tooltipClass . '" title="' . $tooltipTitle . '" name="' . $inputName . '" id="' . $inputName . '" type="text" size="4" value="' . $qtylValue . '">';
print '</td>';
print '<td class="left">';
print $tmpwarehouseObject->getNomUrl(0) . ' / ';
print '<!-- Show details of lot -->';
print '<input name="batchl' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $dbatch->id . '">';
print '<input name="entl' . $indiceAsked . '_'.$subj.'" type="hidden" value="' . $tmpwarehouseObject->id . '">';
//print '|'.$line->fk_product.'|'.$dbatch->batch.'|<br>';
print $langs->trans("Batch") . ': ';
$result = $productlotObject->fetch(0, $line->fk_product, $dbatch->batch);
if ($result > 0) {
print $productlotObject->getNomUrl(1);
} else {
print $langs->trans("TableLotIncompleteRunRepairWithParamStandardEqualConfirmed");
}
if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
print ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
}
if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
print ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
}
print ' (' . $dbatch->qty . ')';
$quantityToBeDelivered -= $deliverableQty;
if ($quantityToBeDelivered < 0) {
$quantityToBeDelivered = 0;
}
//dol_syslog('deliverableQty = '.$deliverableQty.' batchStock = '.$batchStock);
$subj++;
print '</td>';
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td class="left">' . dol_print_date($dbatch->context['stock_entry_date'], 'day') . '</td>';
}
print '</tr>';
}
}
}
}
if ($subj == 0) { // Line not shown yet, we show it
$warehouse_selected_id = GETPOSTINT('entrepot_id');
print '<!-- line not shown yet, we show it -->';
print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
$disabled = '';
$alt = '';
if (isModEnabled('productbatch') && $product->hasbatch()) {
$disabled = 'disabled="disabled"';
$alt = 'Product need serial or batch number';
}
if ($warehouse_selected_id <= 0) { // We did not force a given warehouse, so we won't have no warehouse to change qty.
$disabled = 'disabled="disabled"';
}
// finally we overwrite the input with the product status stockable_product if it's disabled
if ($product->stockable_product == Product::ENABLED_STOCK) {
$disabled = '';
}
print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="0"'.($disabled ? ' '.$disabled : '').($alt ? ' title="'.dolPrintHTMLForAttribute($alt).'"' : '').'> ';
if (empty($disabled) && (!getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER') || $product->stockable_product == Product::DISABLED_STOCK)) {
print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_selected_id . '">';
}
} elseif ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
$disabled = '';
if (isModEnabled('productbatch') && $product->hasbatch()) {
$disabled = 'disabled="disabled"';
}
if ($warehouse_selected_id <= 0) { // We did not force a given warehouse, so we won't have no warehouse to change qty.
$disabled = 'disabled="disabled"';
}
print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="'.$quantityToBeDelivered.'"'.($disabled ? ' '.$disabled : '').'> ';
if (empty($disabled) && !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_selected_id . '">';
}
} else {
print $langs->trans("NA");
}
print '</td>';
print '<td class="left">';
if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
// product may use stock management
if (($warehouse_selected_id > 0 && $product->stockable_product == Product::ENABLED_STOCK)) {
$warehouseObject = new Entrepot($db);
$warehouseObject->fetch($warehouse_selected_id);
print img_warning() . ' ' . $langs->trans("NoProductToShipFoundIntoStock", $warehouseObject->label);
} else {
if ($line->fk_product) {
if ($product->stockable_product != Product::ENABLED_STOCK) {
print img_warning() . ' ' . $langs->trans('StockDisabled');
} else {
print img_warning() . ' ' . $langs->trans('StockTooLow');
}
} else {
print '';
}
}
} else {
print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
}
print '</td>';
if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
print '<td></td>';
} //StockEntrydate
print '</tr>';
}
}
} elseif (empty($reshook) && $line->special_code == SUBTOTALS_SPECIAL_CODE && !in_array($line->id, $title_lines_to_disable)) {
require dol_buildpath('/core/tpl/subtotalline_select.tpl.php');
}
$indiceAsked++;
}
print "</table>";
print '</div>';
print '<br>';
print $form->buttonsSaveCancel("Create");
print '</form>';
print '<br>';
} else {
dol_print_error($db);
}
}
} elseif ($object->id > 0) {
'@phan-var-force Expedition $object'; // Need to force it (type overridden earlier)
// Edit and view mode
$lines = $object->lines;
$num_prod = count($lines);
if (!empty($object->origin) && $object->origin_id > 0) {
$typeobject = $object->origin;
$origin = (string) $object->origin; // Cast to string because origin is type hinted as object too.
$origin_id = $object->origin_id;
$object->fetch_origin(); // Load property $object->origin_object (old $object->commande, $object->propal, ...)
}
$soc = new Societe($db);
$soc->fetch($object->socid);
$res = $object->fetch_optionals();
$head = shipping_prepare_head($object);
print dol_get_fiche_head($head, 'shipping', $langs->trans("Shipment"), -1, $object->picto, 0, '', '', 0, '', 1);
$formconfirm = '';
// Confirm deletion
if ($action == 'delete') {
$formquestion = array();
if ($object->status == Expedition::STATUS_CLOSED && getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
$formquestion = array(
array(
'label' => $langs->trans('ShipmentIncrementStockOnDelete'),
'name' => 'alsoUpdateStock',
'type' => 'checkbox',
'value' => 0
),
);
}
$formconfirm = $form->formconfirm(
$_SERVER['PHP_SELF'] . '?id=' . $object->id,
$langs->trans('DeleteSending'),
$langs->trans("ConfirmDeleteSending", $object->ref),
'confirm_delete',
$formquestion,
0,
1
);
}
// Confirm delete of subtotal line
if ($action == 'ask_subtotal_deleteline') {
$lineid = GETPOSTINT('lineid');
$langs->load("subtotals");
$title = "DeleteSubtotalLine";
$question = "ConfirmDeleteSubtotalLine";
if (GETPOST('type') == 'title') {
$formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
$title = "DeleteTitleLine";
$question = "ConfirmDeleteTitleLine";
}
$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
}
// Confirm validation
if ($action == 'valid') {
$objectref = substr($object->ref, 1, 4);
if ($objectref == 'PROV') {
$numref = $object->getNextNumRef($soc);
} else {
$numref = (string) $object->ref;
}
$text = $langs->trans("ConfirmValidateSending", $numref);
if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
$text .= '<br>' . img_picto('', 'movement', 'class="pictofixedwidth"') . $langs->trans("StockMovementWillBeRecorded") . '.';
} elseif (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
$text .= '<br>' . img_picto('', 'movement', 'class="pictofixedwidth"') . $langs->trans("StockMovementNotYetRecorded") . '.';
}
if (isModEnabled('notification')) {
require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
$notify = new Notify($db);
$text .= '<br>';
$text .= $notify->confirmMessage('SHIPPING_VALIDATE', $object->socid, $object);
}
$formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('ValidateSending'), $text, 'confirm_valid', '', 0, 1, 260);
}
// Confirm cancellation
if ($action == 'cancel') {
$formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('CancelSending'), $langs->trans("ConfirmCancelSending", $object->ref), 'confirm_cancel', '', 0, 1);
}
// Confirm sign
if ($action == 'sign') {
$text = $langs->trans('ConfirmSignShipping');
if (isModEnabled('notification')) {
require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
$notify = new Notify($db);
$text .= '<br>';
$text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
}
$formquestion = [];
$formquestion[] = [
'type' => 'select',
'name' => 'signed_status',
'select_show_empty' => 0,
'label' => '<span class="fieldrequired">' . $langs->trans('SignStatus') . '</span>',
'values' => $object->getSignedStatusLocalisedArray()
];
$formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('SignShipping'), $text, 'confirm_sign', $formquestion, 0, 1);
}
// Confirm unsign
if ($action == 'unsign') {
$text = $langs->trans('ConfirmUnsignShipping');
if (isModEnabled('notification')) {
require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
$notify = new Notify($db);
$text .= '<br>';
$text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
}
$formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('UnsignShipping'), $text, 'confirm_unsign', '', 0, 1);
}
// Call Hook formConfirm
$parameters = array('formConfirm' => $formconfirm);
$reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
if (empty($reshook)) {
$formconfirm .= $hookmanager->resPrint;
} elseif ($reshook > 0) {
$formconfirm = $hookmanager->resPrint;
}
// Print form confirm
print $formconfirm;
// Calculate totalWeight and totalVolume for all products
// by adding weight and volume of each product line.
$tmparray = $object->getTotalWeightVolume();
$totalWeight = $tmparray['weight'];
$totalVolume = $tmparray['volume'];
if (!empty($typeobject) && $typeobject === 'commande' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled('order')) {
$objectsrc = new Commande($db);
$objectsrc->fetch($object->origin_object->id);
}
if (!empty($typeobject) && $typeobject === 'propal' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled("propal")) {
$objectsrc = new Propal($db);
$objectsrc->fetch($object->origin_object->id);
}
// Shipment card
$linkback = '<a href="' . DOL_URL_ROOT . '/expedition/list.php?restore_lastsearch_values=1' . (!empty($socid) ? '&socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
$morehtmlref = '<div class="refidno">';
// Ref customer shipment
$morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->hasRight('expedition', 'creer'), 'string', '', 0, 1);
$morehtmlref .= $form->editfieldval("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->hasRight('expedition', 'creer'), 'string' . (isset($conf->global->THIRDPARTY_REF_INPUT_SIZE) ? ':' . getDolGlobalString('THIRDPARTY_REF_INPUT_SIZE') : ''), '', null, null, '', 1);
// Thirdparty
$morehtmlref .= '<br>' . $object->thirdparty->getNomUrl(1);
// Project
if ($origin && $origin_id > 0) {
if (isModEnabled('project')) {
$langs->load("projects");
$morehtmlref .= '<br>';
if (0) { // @phpstan-ignore-line Do not change on shipment
$morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
if ($action != 'classify') {
$morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
}
$morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $objectsrc->socid, (string) $objectsrc->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
} else {
if (!empty($objectsrc) && !empty($objectsrc->fk_project)) {
$proj = new Project($db);
$proj->fetch($objectsrc->fk_project);
$morehtmlref .= $proj->getNomUrl(1);
if ($proj->title) {
$morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
}
}
}
}
} elseif (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
// Project
if (isModEnabled('project')) {
$langs->load("projects");
$morehtmlref .= '<br>';
if ($permissiontoadd) {
$morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
if ($action != 'classify') {
$morehtmlref .= '<a class="editfielda" href="' . $_SERVER['PHP_SELF'] . '?action=classify&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> ';
}
$morehtmlref .= $form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, (string) $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
} else {
if (!empty($object->fk_project)) {
$proj = new Project($db);
$proj->fetch($object->fk_project);
$morehtmlref .= $proj->getNomUrl(1);
if ($proj->title) {
$morehtmlref .= '<span class="opacitymedium"> - ' . dol_escape_htmltag($proj->title) . '</span>';
}
}
}
}
}
$morehtmlref .= '</div>';
dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
print '<div class="fichecenter">';
print '<div class="fichehalfleft">';
print '<div class="underbanner clearboth"></div>';
print '<table class="border tableforfield centpercent">';
// Linked documents
if (!empty($typeobject) && $typeobject == 'commande' && $object->origin_object->id && isModEnabled('order')) {
print '<tr><td>';
print $langs->trans("RefOrder") . '</td>';
print '<td>';
print $objectsrc->getNomUrl(1, 'commande');
print "</td>\n";
print '</tr>';
}
if (!empty($typeobject) && $typeobject == 'propal' && $object->origin_object->id && isModEnabled("propal")) {
print '<tr><td>';
print $langs->trans("RefProposal") . '</td>';
print '<td>';
print $objectsrc->getNomUrl(1, 'expedition');
print "</td>\n";
print '</tr>';
}
// Date creation
print '<tr><td class="titlefieldmiddle">' . $langs->trans("DateCreation") . '</td>';
print '<td>' . dol_print_date($object->date_creation, "dayhour") . "</td>\n";
print '</tr>';
// Delivery date planned
print '<tr><td height="10">';
print '<table class="nobordernopadding centpercent"><tr><td>';
print $langs->trans('DateDeliveryPlanned');
print '</td>';
if ($action != 'editdate_livraison') {
print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editdate_livraison&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetDeliveryDate'), 1) . '</a></td>';
}
print '</tr></table>';
print '</td><td>';
if ($action == 'editdate_livraison') {
print '<form name="setdate_livraison" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
print '<input type="hidden" name="token" value="' . newToken() . '">';
print '<input type="hidden" name="action" value="setdate_livraison">';
print $form->selectDate($object->date_delivery ? $object->date_delivery : -1, 'liv_', 1, 1, 0, "setdate_livraison", 1, 0);
print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
print '</form>';
} else {
print $object->date_delivery ? dol_print_date($object->date_delivery, 'dayhour') : '&nbsp;';
}
print '</td>';
print '</tr>';
// Delivery sending date
print '<tr><td height="10">';
print '<table class="nobordernopadding centpercent"><tr><td>';
print $langs->trans('DateShipping');
print '</td>';
if ($action != 'editdate_shipping') {
print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editdate_shipping&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetShippingDate'), 1) . '</a></td>';
}
print '</tr></table>';
print '</td><td>';
if ($action == 'editdate_shipping') {
print '<form name="setdate_shipping" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
print '<input type="hidden" name="token" value="' . newToken() . '">';
print '<input type="hidden" name="action" value="setdate_shipping">';
print $form->selectDate($object->date_shipping ? $object->date_shipping : -1, 'ship_', 1, 1, 0, "setdate_shipping", 1, 0);
print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
print '</form>';
} else {
print $object->date_shipping ? dol_print_date($object->date_shipping, 'dayhour') : '&nbsp;';
}
print '</td>';
print '</tr>';
// Weight
print '<tr><td>';
print $form->editfieldkey("Weight", 'trueWeight', $object->trueWeight, $object, $user->hasRight('expedition', 'creer'));
print '</td><td>';
if ($action == 'edittrueWeight') {
print '<form name="settrueweight" action="' . $_SERVER["PHP_SELF"] . '" method="post">';
print '<input name="action" value="settrueWeight" type="hidden">';
print '<input name="id" value="' . $object->id . '" type="hidden">';
print '<input type="hidden" name="token" value="' . newToken() . '">';
print '<input id="trueWeight" name="trueWeight" value="' . $object->trueWeight . '" type="text" class="width50 valignmiddle">';
print $formproduct->selectMeasuringUnits("weight_units", "weight", (string) $object->weight_units, 0, 2, 'maxwidth125 valignmiddle');
print ' <input class="button smallpaddingimp valignmiddle" name="modify" value="' . $langs->trans("Modify") . '" type="submit">';
print ' <input class="button button-cancel smallpaddingimp valignmiddle" name="cancel" value="' . $langs->trans("Cancel") . '" type="submit">';
print '</form>';
} else {
print $object->trueWeight;
print ($object->trueWeight && $object->weight_units != '') ? ' ' . measuringUnitString(0, "weight", $object->weight_units) : '';
}
// Calculated
if ($totalWeight > 0) {
if (!empty($object->trueWeight)) {
print ' (' . $langs->trans("SumOfProductWeights") . ': ';
}
print showDimensionInBestUnit($totalWeight, 0, "weight", $langs, getDolGlobalInt('MAIN_WEIGHT_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT', 'no'));
if (!empty($object->trueWeight)) {
print ')';
}
}
print '</td></tr>';
// Width
print '<tr><td>' . $form->editfieldkey("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
print $form->editfieldval("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer'));
print ($object->trueWidth && $object->width_units != '') ? ' ' . measuringUnitString(0, "size", $object->width_units) : '';
print '</td></tr>';
// Height
print '<tr><td>' . $form->editfieldkey("Height", 'trueHeight', $object->trueHeight, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
if ($action == 'edittrueHeight') {
print '<form name="settrueHeight" action="' . $_SERVER["PHP_SELF"] . '" method="post">';
print '<input name="action" value="settrueHeight" type="hidden">';
print '<input name="id" value="' . $object->id . '" type="hidden">';
print '<input type="hidden" name="token" value="' . newToken() . '">';
print '<input id="trueHeight" name="trueHeight" value="' . $object->trueHeight . '" type="text" class="width50">';
print $formproduct->selectMeasuringUnits("size_units", "size", $object->size_units, 0, 2);
print ' <input class="button smallpaddingimp" name="modify" value="' . $langs->trans("Modify") . '" type="submit">';
print ' <input class="button button-cancel smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '" type="submit">';
print '</form>';
} else {
print $object->trueHeight;
print ($object->trueHeight && $object->height_units != '') ? ' ' . measuringUnitString(0, "size", $object->height_units) : '';
}
print '</td></tr>';
// Depth
print '<tr><td>' . $form->editfieldkey("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
print $form->editfieldval("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer'));
print ($object->trueDepth && $object->depth_units != '') ? ' ' . measuringUnitString(0, "size", $object->depth_units) : '';
print '</td></tr>';
// Volume
print '<tr><td>';
print $langs->trans("Volume");
print '</td>';
print '<td>';
$calculatedVolume = 0;
$volumeUnit = 0;
if ($object->trueWidth && $object->trueHeight && $object->trueDepth) {
$calculatedVolume = ($object->trueWidth * $object->trueHeight * $object->trueDepth);
$volumeUnit = $object->size_units * 3;
}
// If sending volume not defined we use sum of products
if ($calculatedVolume > 0) {
if ($volumeUnit < 50) {
print showDimensionInBestUnit($calculatedVolume, $volumeUnit, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
} else {
print $calculatedVolume . ' ' . measuringUnitString(0, "volume", $volumeUnit);
}
}
if ($totalVolume > 0) {
if ($calculatedVolume) {
print ' (' . $langs->trans("SumOfProductVolumes") . ': ';
}
print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
//if (empty($calculatedVolume)) print ' ('.$langs->trans("Calculated").')';
if ($calculatedVolume) {
print ')';
}
}
print "</td>\n";
print '</tr>';
// Other attributes
//$cols = 2;
include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
print '</table>';
print '</div>';
print '<div class="fichehalfright">';
print '<div class="underbanner clearboth"></div>';
print '<table class="border centpercent tableforfield">';
// Sending method
print '<tr><td>';
print '<table class="nobordernopadding centpercent"><tr><td>';
print $langs->trans('SendingMethod');
print '</td>';
if ($action != 'editshipping_method_id' && $permissiontoadd) {
print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editshipping_method_id&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetSendingMethod'), 1) . '</a></td>';
}
print '</tr></table>';
print '</td><td>';
if ($action == 'editshipping_method_id') {
print '<form name="setshipping_method_id" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
print '<input type="hidden" name="token" value="' . newToken() . '">';
print '<input type="hidden" name="action" value="setshipping_method_id">';
$object->fetch_delivery_methods();
print $form->selectarray("shipping_method_id", $object->meths, $object->shipping_method_id, 1, 0, 0, "", 1);
if ($user->admin) {
print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
}
print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
print '</form>';
} else {
if ($object->shipping_method_id > 0) {
// Get code using getLabelFromKey
$code = $langs->getLabelFromKey($db, (string) $object->shipping_method_id, 'c_shipment_mode', 'rowid', 'code');
print $langs->trans("SendingMethod" . strtoupper($code));
}
}
print '</td>';
print '</tr>';
// Tracking Number
print '<tr><td class="titlefieldmiddle">' . $form->editfieldkey("TrackingNumber", 'tracking_number', $object->tracking_number, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
print $form->editfieldval("TrackingNumber", 'tracking_number', $object->tracking_url, $object, $user->hasRight('expedition', 'creer'), 'safehtmlstring', $object->tracking_number);
print '</td></tr>';
// Incoterms
if (isModEnabled('incoterm')) {
print '<tr><td>';
print '<table class="nobordernopadding centpercent"><tr><td>';
print $langs->trans('IncotermLabel');
print '<td><td class="right">';
if ($permissiontoadd) {
print '<a class="editfielda" href="' . DOL_URL_ROOT . '/expedition/card.php?id=' . $object->id . '&action=editincoterm&token=' . newToken() . '">' . img_edit() . '</a>';
} else {
print '&nbsp;';
}
print '</td></tr></table>';
print '</td>';
print '<td>';
if ($action != 'editincoterm') {
print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
} else {
print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'] . '?id=' . $object->id);
}
print '</td></tr>';
}
// Other attributes
$parameters = array();
$reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
print $hookmanager->resPrint;
print "</table>";
print '</div>';
print '</div>';
print '<div class="clearboth"></div>';
print dol_get_fiche_end();
/*
* Lines of simple shipment
*/
if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
if (!empty($object->table_element_line)) {
// Show object lines
$result = $object->getLinesArray();
print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? '' : '#line_'.GETPOSTINT('lineid')).'" method="POST">
<input type="hidden" name="token" value="' . newToken().'">
<input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
<input type="hidden" name="mode" value="">
<input type="hidden" name="page_y" value="">
<input type="hidden" name="id" value="' . $object->id.'">
';
if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php';
}
print '<div class="div-table-responsive-no-min">';
if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
print '<table id="tablelines" class="noborder noshadow" width="100%">';
}
if (!empty($object->lines)) {
$object->printObjectLines($action, $mysoc, null, GETPOSTINT('lineid'), 1, '/expedition/tpl');
}
// Form to add new line
if ($object->status == 0 && $permissiontoadd && $action != 'selectlines') {
if ($action != 'editline') {
// Add products/services form
$parameters = array();
$reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
if ($reshook < 0) {
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
}
if (empty($reshook)) {
$object->formAddObjectLine(1, $mysoc, $soc);
}
}
}
if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
print '</table>';
}
print '</div>';
print "</form>\n";
}
}
// Lines of products of origin
if (!empty($object->origin) && $object->origin_id > 0) {
if ($action == 'editline') {
print ' <form name="updateline" id="updateline" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&amp;lineid=' . $line_id . '" method="POST">
<input type="hidden" name="token" value="' . newToken() . '">
<input type="hidden" name="action" value="updateline">
<input type="hidden" name="mode" value="">
<input type="hidden" name="id" value="' . $object->id . '">
';
}
print '<br>';
if (!empty($conf->use_javascript_ajax) && $object->status == Expedition::STATUS_DRAFT && $usercancreate && $num_prod > 1) {
include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
}
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent" id="tablelines" >';
print '<thead>';
print '<tr class="liste_titre">';
// Adds a line numbering column
if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
print '<td width="5" class="center linecolnum">&nbsp;</td>';
}
// Product/Service
print '<td class="linecoldescription" >' . $langs->trans("Products") . '</td>';
// Qty
print '<td class="center linecolqty">' . $langs->trans("QtyOrdered") . '</td>';
if ($origin_id > 0) {
print '<td class="center linecolqtyinothershipments">' . $langs->trans("QtyInOtherShipments") . '</td>';
}
if ($action == 'editline') {
$editColspan = 3;
if (!isModEnabled('stock')) {
$editColspan--;
}
if (!isModEnabled('productbatch')) {
$editColspan--;
}
print '<td class="center linecoleditlineotherinfo" colspan="' . $editColspan . '">';
if ($object->status <= 1) {
print $langs->trans("QtyToShip");
} else {
print $langs->trans("QtyShipped");
}
if (isModEnabled('stock')) {
print ' - ' . $langs->trans("WarehouseSource");
}
if (isModEnabled('productbatch')) {
print ' - ' . $langs->trans("Batch");
}
print '</td>';
} else {
if ($object->status <= 1) {
print '<td class="center linecolqtytoship">' . $langs->trans("QtyToShip") . '</td>';
} else {
print '<td class="center linecolqtyshipped">' . $langs->trans("QtyShipped") . '</td>';
}
if (isModEnabled('stock')) {
print '<td class="left linecolwarehousesource">' . $langs->trans("WarehouseSource") . '</td>';
}
if (isModEnabled('productbatch')) {
print '<td class="left linecolbatch">' . $langs->trans("Batch") . '</td>';
}
}
print '<td class="center linecolweight">' . $langs->trans("CalculatedWeight") . '</td>';
print '<td class="center linecolvolume">' . $langs->trans("CalculatedVolume") . '</td>';
//print '<td class="center">'.$langs->trans("Size").'</td>';
if ($object->status == 0) {
print '<td class="linecoledit"></td>';
print '<td class="linecoldelete" width="10"></td>';
print '<td class="linecolmove"></td>';
}
print "</tr>\n";
print '</thead>';
$outputlangs = $langs;
if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
$object->fetch_thirdparty();
$newlang = '';
if (/* empty($newlang) && */GETPOST('lang_id', 'aZ09')) {
$newlang = GETPOST('lang_id', 'aZ09');
}
if (empty($newlang)) {
$newlang = $object->thirdparty->default_lang;
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
}
// Get list of products already sent for same source object into $alreadysent
$alreadysent = array();
if ($origin_id > 0) {
$sql = "SELECT obj.rowid, obj.fk_product, obj.label, obj.description, obj.product_type as fk_product_type, obj.qty as qty_asked, obj.fk_unit, obj.date_start, obj.date_end, obj.special_code";
$sql .= ", ed.rowid as shipmentline_id, ed.qty as qty_shipped, ed.fk_expedition as expedition_id, ed.fk_elementdet, ed.fk_entrepot";
$sql .= ", e.rowid as shipment_id, e.ref as shipment_ref, e.date_creation, e.date_valid, e.date_delivery, e.date_expedition";
$sql .= ', p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid, p.tosell as product_tosell, p.tobuy as product_tobuy, p.tobatch as product_tobatch';
$sql .= ', p.description as product_desc';
$sql .= " FROM " . MAIN_DB_PREFIX . "expeditiondet as ed";
$sql .= ", " . MAIN_DB_PREFIX . "expedition as e";
$sql .= ", " . MAIN_DB_PREFIX . $db->sanitize((string) $origin) . "det as obj";
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product as p ON obj.fk_product = p.rowid";
$sql .= " WHERE e.entity IN (" . getEntity('expedition') . ")";
$sql .= " AND obj.fk_" . $db->sanitize((string) $origin) . " = " . ((int) $origin_id);
$sql .= " AND obj.rowid = ed.fk_elementdet";
$sql .= " AND ed.fk_expedition = e.rowid";
//if ($filter) $sql.= $filter;
$sql .= " ORDER BY obj.fk_product";
dol_syslog("expedition/card.php get list of shipment lines", LOG_DEBUG);
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
if ($obj) {
// $obj->rowid is rowid in $origin."det" table
$alreadysent[$obj->rowid][$obj->shipmentline_id] = array(
'shipment_ref' => $obj->shipment_ref,
'shipment_id' => $obj->shipment_id,
'warehouse' => $obj->fk_entrepot,
'qty_shipped' => $obj->qty_shipped,
'product_tosell' => $obj->product_tosell,
'product_tobuy' => $obj->product_tobuy,
'product_tobatch' => $obj->product_tobatch,
'date_valid' => $db->jdate($obj->date_valid),
'date_delivery' => $db->jdate($obj->date_delivery));
}
$i++;
}
}
//var_dump($alreadysent);
}
print '<tbody>';
// Loop on each product to send/sent
$conf->cache['product'] = array();
$conf->cache['warehouse'] = array();
for ($i = 0; $i < $num_prod; $i++) {
$parameters = array('i' => $i, 'line' => $lines[$i], 'line_id' => $line_id, 'num' => $num_prod, 'alreadysent' => $alreadysent, 'editColspan' => !empty($editColspan) ? $editColspan : 0, 'outputlangs' => $outputlangs);
$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
if ($reshook < 0) {
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
}
if (empty($reshook) && $lines[$i]->product_type != "9") {
print '<!-- origin line id = ' . $lines[$i]->origin_line_id . ' -->'; // id of order line
print '<tr class="oddeven" id="row-' . $lines[$i]->id . '" data-id="' . $lines[$i]->id . '" data-element="' . $lines[$i]->element . '" >';
$line = $lines[$i];
$line->fetch_optionals();
// #
if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
print '<td class="center linecolnum">' . ($i + 1) . '</td>';
}
// Predefined product or service
if ($lines[$i]->fk_product > 0) {
// Define output language
if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
$product_id = $lines[$i]->fk_product;
if (!isset($conf->cache['product'][$product_id])) {
$prod = new Product($db);
$prod->fetch($product_id);
$conf->cache['product'][$product_id] = $prod;
} else {
$prod = $conf->cache['product'][$product_id];
}
$label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $lines[$i]->product_label;
} else {
$label = (!empty($lines[$i]->label) ? $lines[$i]->label : $lines[$i]->product_label);
}
print '<td class="linecoldescription">';
// Show product and description
$product_static->type = $lines[$i]->fk_product_type;
$product_static->id = $lines[$i]->fk_product;
$product_static->ref = $lines[$i]->ref;
$product_static->status = $lines[$i]->product_tosell;
$product_static->status_buy = $lines[$i]->product_tobuy;
$product_static->status_batch = $lines[$i]->product_tobatch;
$product_static->weight = $lines[$i]->weight;
$product_static->weight_units = $lines[$i]->weight_units;
$product_static->length = $lines[$i]->length;
$product_static->length_units = $lines[$i]->length_units;
$product_static->width = !empty($lines[$i]->width) ? $lines[$i]->width : 0;
$product_static->width_units = !empty($lines[$i]->width_units) ? $lines[$i]->width_units : 0;
$product_static->height = !empty($lines[$i]->height) ? $lines[$i]->height : 0;
$product_static->height_units = !empty($lines[$i]->height_units) ? $lines[$i]->height_units : 0;
$product_static->surface = $lines[$i]->surface;
$product_static->surface_units = $lines[$i]->surface_units;
$product_static->volume = $lines[$i]->volume;
$product_static->volume_units = $lines[$i]->volume_units;
$product_static->stockable_product = $lines[$i]->stockable_product;
$text = $product_static->getNomUrl(1);
$text .= ' - ' . $label;
$description = (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : dol_htmlentitiesbr($lines[$i]->description));
print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
print_date_range(!empty($lines[$i]->date_start) ? $lines[$i]->date_start : '', !empty($lines[$i]->date_end) ? $lines[$i]->date_end : '');
if (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE')) {
print (!empty($lines[$i]->description) && $lines[$i]->description != $lines[$i]->product) ? '<br>' . dol_htmlentitiesbr($lines[$i]->description) : '';
}
$lineExtrafieldsHtml = $line->showOptionals($extrafields, ($action == 'editline' && $line->id == $line_id) ? 'edit' : 'view', array(), '', '', '1', 'line');
if (!empty($lineExtrafieldsHtml)) {
print '<div style="padding-top: 10px" id="extrafield_lines_area_' . $line->id . '" name="extrafield_lines_area_' . $line->id . '">';
print $lineExtrafieldsHtml;
print '</div>';
}
print "</td>\n";
} else {
print '<td class="linecoldescription" >';
if ($lines[$i]->product_type == Product::TYPE_SERVICE) {
$text = img_object($langs->trans('Service'), 'service');
} else {
$text = img_object($langs->trans('Product'), 'product');
}
if (!empty($lines[$i]->label)) {
$text .= ' <strong>' . $lines[$i]->label . '</strong>';
print $form->textwithtooltip($text, $lines[$i]->description, 3, 0, '', (string) $i);
} else {
print $text . ' ' . nl2br($lines[$i]->description);
}
print_date_range($lines[$i]->date_start, $lines[$i]->date_end);
$lineExtrafieldsHtml = $line->showOptionals($extrafields, ($action == 'editline' && $line->id == $line_id) ? 'edit' : 'view', array(), '', '', '1', 'line');
if (!empty($lineExtrafieldsHtml)) {
print '<div style="padding-top: 10px" id="extrafield_lines_area_' . $line->id . '" name="extrafield_lines_area_' . $line->id . '">';
print $lineExtrafieldsHtml;
print '</div>';
}
print "</td>\n";
}
$unit_order = '';
if (getDolGlobalString('PRODUCT_USE_UNITS')) {
$unit_order = measuringUnitString((int) $lines[$i]->fk_unit);
}
// Qty ordered
print '<td class="center linecolqty">' . $lines[$i]->qty_asked . ' ' . $unit_order . '</td>';
// Qty in other shipments (with shipment and warehouse used)
if ($origin_id > 0) {
print '<td class="linecolqtyinothershipments center nowrap">';
$htmltooltip = '';
$qtyalreadysent = 0;
foreach ($alreadysent as $key => $val) {
if ($lines[$i]->fk_elementdet == $key) {
$j = 0;
foreach ($val as $shipmentline_id => $shipmentline_var) {
if ($shipmentline_var['shipment_id'] == $lines[$i]->fk_expedition) {
continue; // We want to show only "other shipments"
}
$j++;
if ($j > 1) {
$htmltooltip .= '<br>';
}
$shipment_static->fetch($shipmentline_var['shipment_id']);
$htmltooltip .= $shipment_static->getNomUrl(1, '', 0, 0, 1);
$htmltooltip .= ' - ' . $shipmentline_var['qty_shipped'];
$htmltooltip .= ' - ' . $langs->trans("DateValidation") . ' : ' . (empty($shipmentline_var['date_valid']) ? $langs->trans("Draft") : dol_print_date($shipmentline_var['date_valid'], 'dayhour'));
/*if (isModEnabled('stock') && $shipmentline_var['warehouse'] > 0) {
$warehousestatic->fetch($shipmentline_var['warehouse']);
$htmltext .= '<br>'.$langs->trans("FromLocation").' : '.$warehousestatic->getNomUrl(1, '', 0, 1);
}*/
//print ' '.$form->textwithpicto('', $htmltext, 1);
$qtyalreadysent += $shipmentline_var['qty_shipped'];
}
if ($j) {
$htmltooltip = $langs->trans("QtyInOtherShipments") . '...<br><br>' . $htmltooltip . '<br><input type="submit" name="dummyhiddenbuttontogetfocus" style="display:none" autofocus>';
}
}
}
print $form->textwithpicto((string) $qtyalreadysent, $htmltooltip, 1, 'info', '', 0, 3, 'tooltip' . $lines[$i]->id);
print '</td>';
}
if ($action == 'editline' && $lines[$i]->id == $line_id) {
// edit mode
print '<td colspan="' . $editColspan . '" class="center"><table class="nobordernopadding centpercent">';
if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
print '<!-- case edit 1 -->';
$line = new ExpeditionLigne($db);
foreach ($lines[$i]->detail_batch as $detail_batch) {
print '<tr>';
// Qty to ship or shipped
print '<td><input class="qtyl right" name="qtyl' . $detail_batch->fk_expeditiondet . '_' . $detail_batch->id . '" id="qtyl' . $line_id . '_' . $detail_batch->id . '" type="text" size="4" value="' . $detail_batch->qty . '"></td>';
// Batch number management
if ($lines[$i]->entrepot_id == 0) {
// only show lot numbers from src warehouse when shipping from multiple warehouses
$line->fetch($detail_batch->fk_expeditiondet);
}
$entrepot_id = !empty($detail_batch->entrepot_id) ? $detail_batch->entrepot_id : $lines[$i]->entrepot_id;
print '<td>' . $formproduct->selectLotStock($detail_batch->fk_origin_stock, 'batchl' . $detail_batch->fk_expeditiondet . '_' . $detail_batch->fk_origin_stock, '', 1, 0, $lines[$i]->fk_product, $entrepot_id) . '</td>';
print '</tr>';
}
// add a 0 qty lot row to be able to add a lot
print '<tr>';
// Qty to ship or shipped
print '<td><input class="qtyl" name="qtyl' . $line_id . '_0" id="qtyl' . $line_id . '_0" type="text" size="4" value="0"></td>';
// Batch number management
print '<td>' . $formproduct->selectLotStock('', 'batchl' . $line_id . '_0', '', 1, 0, $lines[$i]->fk_product) . '</td>';
print '</tr>';
} elseif (isModEnabled('stock')) {
if ($lines[$i]->fk_product > 0) {
if ($lines[$i]->entrepot_id > 0) {
print '<!-- case edit 2 -->';
print '<tr>';
// Qty to ship or shipped
print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '">' . $unit_order . '</td>';
// Warehouse source
print '<td>' . $formproduct->selectWarehouses($lines[$i]->entrepot_id, 'entl' . $line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200') . '</td>';
// Batch number management
print '<td>';
if (isModEnabled('productbatch')) {
print ' - ' . $langs->trans("NA");
}
print '</td>';
print '</tr>';
} elseif (count($lines[$i]->details_entrepot) > 1) {
print '<!-- case edit 3 -->';
foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
print '<tr>';
// Qty to ship or shipped
print '<td><input class="qtyl right" name="qtyl' . $detail_entrepot->line_id . '" id="qtyl' . $detail_entrepot->line_id . '" type="text" size="4" value="' . $detail_entrepot->qty_shipped . '">' . $unit_order . '</td>';
// Warehouse source
print '<td>' . $formproduct->selectWarehouses($detail_entrepot->entrepot_id, 'entl' . $detail_entrepot->line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200') . '</td>';
// Batch number management
print '<td>';
if (isModEnabled('productbatch')) {
print ' - ' . $langs->trans("NA");
}
print '</td>';
print '</tr>';
}
} elseif ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
print '<!-- case edit 4 -->';
print '<tr>';
// Qty to ship or shipped
print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '"></td>';
print '<td><span class="opacitymedium">(' . $langs->trans("Service") . ')</span></td>';
print '<td></td>';
print '</tr>';
} else {
print '<!-- case edit 5 -->';
print '<tr><td colspan="3">' . $langs->trans("ErrorStockIsNotEnough") . '</td></tr>';
}
} else {
print '<!-- case edit 6 -->';
print '<tr>';
// Qty to ship or shipped
print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '">' . $unit_order . '</td>';
// Warehouse source
print '<td></td>';
// Batch number management
print '<td></td>';
print '</tr>';
}
} else { // both product batch and stock are not activated.
print '<!-- case edit 7 -->';
print '<tr>';
// Qty to ship or shipped
print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '"></td>';
// Warehouse source
print '<td></td>';
// Batch number management
print '<td></td>';
print '</tr>';
}
print '</table></td>';
} else {
// Qty to ship or shipped
print '<td class="linecolqtytoship center">' . $lines[$i]->qty_shipped . ' ' . $unit_order . '</td>';
// Warehouse source
if (isModEnabled('stock')) {
print '<td class="linecolwarehousesource tdoverflowmax200">';
if ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
} elseif ($lines[$i]->entrepot_id > 0 && $lines[$i]->stockable_product == Product::ENABLED_STOCK) {
$warehouse_id = $lines[$i]->entrepot_id;
if (!isset($conf->cache['warehouse'][$warehouse_id])) {
$warehouse = new Entrepot($db);
$warehouse->fetch($warehouse_id);
$conf->cache['warehouse'][$warehouse_id] = $warehouse;
} else {
$warehouse = $conf->cache['warehouse'][$warehouse_id];
}
print $warehouse->getNomUrl(1);
} elseif (count($lines[$i]->details_entrepot) > 1) {
$detail = '';
foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
$warehouse_id = $detail_entrepot->entrepot_id;
if ($warehouse_id > 0) {
if (!isset($conf->cache['warehouse'][$warehouse_id])) {
$warehouse = new Entrepot($db);
$warehouse->fetch($warehouse_id);
$conf->cache['warehouse'][$warehouse_id] = $warehouse;
} else {
$warehouse = $conf->cache['warehouse'][$warehouse_id];
}
$detail .= $langs->trans("DetailWarehouseFormat", $warehouse->label, $detail_entrepot->qty_shipped) . '<br>';
}
}
print $form->textwithtooltip(img_picto('', 'object_stock') . ' ' . $langs->trans("DetailWarehouseNumber"), $detail);
} elseif (count($lines[$i]->detail_children ?? []) > 1) {
$detail = '';
foreach ($lines[$i]->detail_children as $child_product_id => $child_stock_list) {
foreach ($child_stock_list as $warehouse_id => $total_qty) {
// get product from cache
$child_product_label = '';
if (!isset($conf->cache['product'][$child_product_id])) {
$child_product = new Product($db);
$child_product->fetch($child_product_id);
$conf->cache['product'][$child_product_id] = $child_product;
} else {
$child_product = $conf->cache['product'][$child_product_id];
}
$child_product_label = $child_product->ref . ' ' . $child_product->label;
// get warehouse from cache
if (!isset($conf->cache['warehouse'][$warehouse_id])) {
$child_warehouse = new Entrepot($db);
$child_warehouse->fetch($warehouse_id);
$conf->cache['warehouse'][$warehouse_id] = $child_warehouse;
} else {
$child_warehouse = $conf->cache['warehouse'][$warehouse_id];
}
$detail .= $langs->trans('DetailChildrenFormat', $child_product_label, $child_warehouse->label, price2num($total_qty, 'MS')) . '<br>';
}
}
print $form->textwithtooltip(img_picto('', 'object_stock') . ' ' . $langs->trans('DetailWarehouseNumber'), $detail);
}
print '</td>';
}
// Batch number management
if (isModEnabled('productbatch')) {
if (isset($lines[$i]->detail_batch)) {
print '<!-- Detail of lot -->';
print '<td class="linecolbatch">';
if ($lines[$i]->product_tobatch) {
$detail = '';
foreach ($lines[$i]->detail_batch as $dbatch) { // $dbatch is instance of ExpeditionLineBatch
$detail .= $langs->trans("Batch") . ': ' . $dbatch->batch;
if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
$detail .= ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
}
if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
$detail .= ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
}
$detail .= ' - ' . $langs->trans("Qty") . ': ' . $dbatch->qty;
$detail .= '<br>';
}
print $form->textwithtooltip(img_picto('', 'object_barcode') . ' ' . $langs->trans("DetailBatchNumber"), $detail);
} else {
print $langs->trans("NA");
}
print '</td>';
} else {
print '<td class="linecolbatch" ></td>';
}
}
}
// Weight
print '<td class="center linecolweight">';
if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
print $lines[$i]->weight * $lines[$i]->qty_shipped . ' ' . measuringUnitString(0, "weight", $lines[$i]->weight_units);
} else {
print '&nbsp;';
}
print '</td>';
// Volume
print '<td class="center linecolvolume">';
if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
print $lines[$i]->volume * $lines[$i]->qty_shipped . ' ' . measuringUnitString(0, "volume", $lines[$i]->volume_units);
} else {
print '&nbsp;';
}
print '</td>';
// Size
//print '<td class="center">'.$lines[$i]->volume*$lines[$i]->qty_shipped.' '.measuringUnitString(0, "volume", $lines[$i]->volume_units).'</td>';
$showmovecol = ($object->status == Expedition::STATUS_DRAFT);
if ($action == 'editline' && $lines[$i]->id == $line_id) {
print '<td class="center" colspan="2" valign="middle">';
print '<input type="submit" class="button button-save" id="savelinebutton marginbottomonly" name="save" value="' . $langs->trans("Save") . '"><br>';
print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="cancel" value="' . $langs->trans("Cancel") . '"><br>';
print '</td>';
if ($showmovecol) {
print '<td class="linecolmove tdlineupdown center"></td>';
}
} elseif ($showmovecol) {
$edit_url = $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=editline&token=' . newToken() . '&lineid=' . $lines[$i]->id;
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
$product_id = $lines[$i]->fk_product;
if (!isset($conf->cache['product'][$product_id])) {
$product = new Product($db);
$product->fetch($product_id);
$conf->cache['product'][$product_id] = $product;
} else {
$product = $conf->cache['product'][$product_id];
}
if ($product->hasFatherOrChild(1)) {
$edit_url = dol_buildpath('/expedition/dispatch.php?id=' . $object->id, 1);
}
}
// edit-delete buttons
print '<td class="linecoledit center">';
print '<a class="editfielda reposition" href="' . $edit_url . '">' . img_edit() . '</a>';
print '</td>';
print '<td class="linecoldelete" width="10">';
print '<a class="reposition" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=deleteline&token=' . newToken() . '&lineid=' . $lines[$i]->id . '">' . img_delete() . '</a>';
print '</td>';
print '<td class="linecolmove tdlineupdown center">';
if ($usercancreate && $num_prod > 1) {
if ($i > 0) {
print '<a class="lineupdown" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=up&token=' . newToken() . '&rowid=' . $lines[$i]->id . '">' . img_up('default', 0, 'imgupforline') . '</a>';
}
if ($i < $num_prod - 1) {
print '<a class="lineupdown" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=down&token=' . newToken() . '&rowid=' . $lines[$i]->id . '">' . img_down('default', 0, 'imgdownforline') . '</a>';
}
}
print '</td>';
// Display lines extrafields
if (isset($rowExtrafieldsStart, $rowExtrafieldsView, $rowEnd)) { // @phan-suppress-current-line PhanPluginUndeclaredVariableIsset
print $rowExtrafieldsStart;
print $rowExtrafieldsView;
print $rowEnd;
}
}
print "</tr>";
} elseif (empty($reshook) && $lines[$i]->product_type == "9") {
$objectsrc = new OrderLine($db);
$objectsrc->fetch($lines[$i]->origin_line_id);
if ($objectsrc->special_code == SUBTOTALS_SPECIAL_CODE) {
$line = $lines[$i];
require dol_buildpath('/core/tpl/subtotal_expedition_view.tpl.php');
}
}
}
// TODO Show also lines ordered but not delivered
if (empty($num_prod)) {
print '<tr><td colspan="8"><span class="opacitymedium">' . $langs->trans("NoLineGoOnTabToAddSome", $langs->transnoentitiesnoconv("ShipmentDistribution")) . '</span></td></tr>';
}
print '</tbody>';
print "</table>\n";
print '</div>';
if ($action == 'editline') {
print "</form>\n";
}
$object->fetchObjectLinked($object->id, $object->element);
}
/*
* Boutons actions
*/
if (($user->socid == 0) && ($action != 'presend')) {
print '<div class="tabsAction">';
$parameters = array();
$reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
// modified by hook
if (empty($reshook)) {
if ($object->status == Expedition::STATUS_DRAFT && $num_prod > 0) {
if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'creer'))
|| (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'shipping_advance', 'validate'))
) {
print dolGetButtonAction('', $langs->trans('Validate'), 'default', $_SERVER["PHP_SELF"] . '?action=valid&token=' . newToken() . '&id=' . $object->id, '');
} else {
print dolGetButtonAction($langs->trans('NotAllowed'), $langs->trans('Validate'), 'default', $_SERVER['PHP_SELF'] . '#', '', false);
}
}
// 0=draft, 1=validated/delivered, 2=closed/delivered
if ($object->status == Expedition::STATUS_VALIDATED && !getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
if ($user->hasRight('expedition', 'creer')) {
print dolGetButtonAction('', $langs->trans('SetToDraft'), 'default', $_SERVER["PHP_SELF"] . '?action=setdraft&token=' . newToken() . '&id=' . $object->id, '');
}
}
if ($object->status == Expedition::STATUS_CLOSED) {
if ($user->hasRight('expedition', 'creer')) {
print dolGetButtonAction('', $langs->trans('ReOpen'), 'default', $_SERVER["PHP_SELF"] . '?action=reopen&token=' . newToken() . '&id=' . $object->id, '');
}
}
// Send
if (empty($user->socid)) {
if ($object->status > 0) {
if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || $user->hasRight('expedition', 'shipping_advance', 'send')) {
print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER["PHP_SELF"] . '?action=presend&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '');
} else {
print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER['PHP_SELF'] . '#', '', false);
}
}
}
// This is just to generate a delivery receipt when option to do this is on
//var_dump($object->linkedObjectsIds['delivery']);
if (getDolGlobalInt('MAIN_SUBMODULE_DELIVERY') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED) && $user->hasRight('expedition', 'delivery', 'creer') && empty($object->linkedObjectsIds['delivery'])) {
print dolGetButtonAction('', $langs->trans('CreateDeliveryOrder'), 'default', $_SERVER["PHP_SELF"] . '?action=create_delivery&token=' . newToken() . '&id=' . $object->id, '');
}
// Sign (to set to status "Signed" without using the online signature page)
if ($object->status > Expedition::STATUS_DRAFT) {
if ($object->signed_status != Expedition::$SIGNED_STATUSES['STATUS_SIGNED_ALL']) {
print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=sign&token=' . newToken() . '">' . $langs->trans("SignShipping") . '</a></div>';
} else {
print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=unsign&token=' . newToken() . '">' . $langs->trans("UnsignShipping") . '</a></div>';
}
}
// Create bill
if (isModEnabled('invoice') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED)) {
if ($user->hasRight('facture', 'creer')) {
if (getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
print dolGetButtonAction('', $langs->trans('CreateBill'), 'default', DOL_URL_ROOT . '/compta/facture/card.php?action=create&origin=' . $object->element . '&originid=' . $object->id . '&socid=' . $object->socid, '');
}
}
}
// Set Billed and Closed
if ($object->status == Expedition::STATUS_VALIDATED) {
if ($user->hasRight('expedition', 'creer') && $object->status > 0) {
if (!$object->billed && getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
print dolGetButtonAction('', $langs->trans('ClassifyBilled'), 'default', $_SERVER["PHP_SELF"] . '?action=classifybilled&token=' . newToken() . '&id=' . $object->id, '');
}
print dolGetButtonAction('', $langs->trans("Close"), 'default', $_SERVER["PHP_SELF"] . '?action=classifyclosed&token=' . newToken() . '&id=' . $object->id, '');
}
}
// Cancel
if ($object->status == Expedition::STATUS_VALIDATED) {
if ($user->hasRight('expedition', 'creer')) {
print dolGetButtonAction('', $langs->trans('Cancel'), 'danger', $_SERVER["PHP_SELF"] . '?action=cancel&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '');
}
}
// Delete
if ($user->hasRight('expedition', 'supprimer')) {
print dolGetButtonAction('', $langs->trans('Delete'), 'delete', $_SERVER["PHP_SELF"] . '?action=delete&token=' . newToken() . '&id=' . $object->id, '');
}
}
print '</div>';
}
/*
* Documents generated
*/
if ($action != 'presend' && $action != 'editline') {
print '<div class="fichecenter"><div class="fichehalfleft">';
$objectref = dol_sanitizeFileName((string) $object->ref);
$filedir = $conf->expedition->dir_output . "/sending/" . $objectref;
$urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id;
$genallowed = $user->hasRight('expedition', 'lire');
$delallowed = $user->hasRight('expedition', 'creer');
print $formfile->showdocuments('expedition', $objectref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
// Show links to link elements
$tmparray = $form->showLinkToObjectBlock($object, array(), array('shipping'), 1);
$linktoelem = $tmparray['linktoelem'];
$htmltoenteralink = $tmparray['htmltoenteralink'];
print $htmltoenteralink;
$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
// Show online signature link
$useonlinesignature = getDolGlobalInt('EXPEDITION_ALLOW_ONLINESIGN');
if ($object->statut != Expedition::STATUS_DRAFT && $useonlinesignature) {
print '<br><!-- Link to sign -->';
require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
print showOnlineSignatureUrl('expedition', $object->ref, $object) . '<br>';
}
print '</div><div class="fichehalfright">';
// List of actions on element
include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
$formactions = new FormActions($db);
//button to go to messaging from the events box
$MAXEVENT = 10;
$morehtmlcenter = dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT . '/expedition/messaging.php?id=' . $object->id);
$somethingshown = $formactions->showactions($object, 'shipping', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter);
print '</div></div>';
}
/*
* Action presend
*/
//Select mail models is same action as presend
if (GETPOST('modelselected')) {
$action = 'presend';
}
// Presend form
$modelmail = 'shipping_send';
$defaulttopic = 'SendShippingRef';
$diroutput = $conf->expedition->dir_output . '/sending';
$trackid = 'shi' . $object->id;
include DOL_DOCUMENT_ROOT . '/core/tpl/card_presend.tpl.php';
}
// End of page
llxFooter();
$db->close();