Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop

This commit is contained in:
Laurent Destailleur 2021-09-23 12:14:54 +02:00
commit 545b4c56d5
13 changed files with 196 additions and 8 deletions

View File

@ -1974,6 +1974,21 @@ if ($action == 'create') {
$text .= $notify->confirmMessage('PROPAL_VALIDATE', $object->socid, $object);
}
// mandatoryPeriod
$nbMandated = 0;
foreach ($object->lines as $line) {
$res = $line->fetch_product();
if ($res > 0 ) {
if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end) )) {
$nbMandated++;
break;
}
}
}
if ($nbMandated > 0) {
$text .= '<div><span class="clearboth nowraponall warning">'.$langs->trans("mandatoryPeriodNeedTobeSetMsgValidate").'</span></div>';
}
if (!$error) {
$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ValidateProp'), $text, 'confirm_validate', '', 0, 1);
}

View File

@ -1924,6 +1924,21 @@ if ($action == 'create' && $usercancreate) {
);
}
// mandatoryPeriod
$nbMandated = 0;
foreach ($object->lines as $line) {
$res = $line->fetch_product();
if ($res > 0 ) {
if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end) )) {
$nbMandated++;
break;
}
}
}
if ($nbMandated > 0 ) $text .= '<div><span class="clearboth nowraponall warning">'.$langs->trans("mandatoryPeriodNeedTobeSetMsgValidate").'</span></div>';
$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ValidateOrder'), $text, 'confirm_validate', $formquestion, 0, 1, 220);
}

View File

@ -4013,6 +4013,21 @@ if ($action == 'create') {
if ($object->type != Facture::TYPE_CREDIT_NOTE && $object->total_ttc < 0) { // Can happen only if $conf->global->FACTURE_ENABLE_NEGATIVE is on
$text .= '<br>'.img_warning().' '.$langs->trans("ErrorInvoiceOfThisTypeMustBePositive");
}
// mandatoryPeriod
$nbMandated = 0;
foreach ($object->lines as $line) {
$res = $line->fetch_product();
if ($res > 0 ) {
if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end) )) {
$nbMandated++;
break;
}
}
}
if ($nbMandated > 0 ) $text .= '<div><span class="clearboth nowraponall warning">'.$langs->trans("mandatoryPeriodNeedTobeSetMsgValidate").'</span></div>';
$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?facid='.$object->id, $langs->trans('ValidateBill'), $text, 'confirm_valid', $formquestion, (($object->type != Facture::TYPE_CREDIT_NOTE && $object->total_ttc < 0) ? "no" : "yes"), 2);
}

View File

@ -68,6 +68,7 @@ if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf-
if (in_array($object->element, array('propal', 'commande', 'order', 'facture', 'facturerec', 'invoice', 'supplier_proposal', 'order_supplier', 'invoice_supplier'))) {
$colspan++; // With this, there is a column move button
}
//print $object->element;
// Lines for extrafield
$objectline = null;
@ -675,13 +676,26 @@ if (!empty($usemargins) && $user->rights->margins->creer) {
}
?>
<?php if ($this->table_element_line != 'commande_fournisseurdet') { ?>
$("#date_start, #date_end").focusout(function()
{
let type = $(this).attr('type');
let mandatoryP = $(this).attr('mandatoryperiod');
if (type == 1 && mandatoryP == 1 ){
if ( $(this).val() == '' && !$(this).hasClass("error") ) {
$(this).addClass('error');
}else{
$(this).removeClass('error');
}
}
});
<?php } ?>
/* When changing predefined product, we reload list of supplier prices required for margin combo */
$("#idprod, #idprodfournprice").change(function()
{
console.log("Call method change() after change on #idprod or #idprodfournprice (senderissupplier=<?php echo $senderissupplier; ?>). this.val = "+$(this).val());
setforpredef(); // TODO Keep vat combo visible and set it to first entry into list that match result of get_default_tva
jQuery('#trlinefordates').show();
<?php
@ -703,6 +717,26 @@ if (!empty($usemargins) && $user->rights->margins->creer) {
{ 'id': $(this).val(), 'socid': <?php print $object->socid; ?> },
function(data) {
console.log("Load unit price end, we got value "+data.price_ht);
$( '#date_start').removeAttr( "type" );
$( '#date_end' ).removeAttr( "type" );
$('#date_start').attr('type', data.type);
$('#date_end').attr('type', data.type);
$( '#date_start').removeAttr( "mandatoryperiod" );
$( '#date_end' ).removeAttr( "mandatoryperiod" );
$('#date_start').attr('mandatoryperiod', data.mandatory_period);
$('#date_end').attr('mandatoryperiod', data.mandatory_period);
// service and we setted mandatory_period to true
if (data.mandatory_period == 1 && data.type == 1 ) {
jQuery("#date_start").addClass("error");
jQuery("#date_end").addClass("error");
}else{
jQuery("#date_start").removeClass("error");
jQuery("#date_end").removeClass("error");
}
jQuery("#price_ht").val(data.price_ht);
<?php
if (!empty($conf->global->PRODUIT_AUTOFILL_DESC) && $conf->global->PRODUIT_AUTOFILL_DESC == 1) {
@ -984,6 +1018,7 @@ if (!empty($usemargins) && $user->rights->margins->creer) {
jQuery("#np_marginRate, #np_markRate, .np_marginRate, .np_markRate, #units, #title_units").show();
jQuery("#fournprice_predef").hide();
}
function setforpredef() {
console.log("Call setforpredef. We hide some fields and show dates");
jQuery("#select_type").val(-1);

View File

@ -333,9 +333,18 @@ $coldisplay++;
if (isset($conf->global->MAIN_DEFAULT_DATE_START_HOUR)) {
print 'jQuery("#date_starthour").val("'.$conf->global->MAIN_DEFAULT_DATE_START_HOUR.'");';
}
if (isset($conf->global->MAIN_DEFAULT_DATE_START_MIN)) {
print 'jQuery("#date_startmin").val("'.$conf->global->MAIN_DEFAULT_DATE_START_MIN.'");';
}
$res = $line->fetch_product();
if ($res > 0 ) {
if ( $line->product->isMandatoryPeriod() && $line->product->isService()) {
print 'jQuery("#date_start").addClass("error");';
}
}
}
if (!$line->date_end) {
if (isset($conf->global->MAIN_DEFAULT_DATE_END_HOUR)) {
@ -344,6 +353,14 @@ $coldisplay++;
if (isset($conf->global->MAIN_DEFAULT_DATE_END_MIN)) {
print 'jQuery("#date_endmin").val("'.$conf->global->MAIN_DEFAULT_DATE_END_MIN.'");';
}
$res = $line->fetch_product();
// on doit fetch le product là !!! pour connaître le type
if ($res > 0 ) {
if ($line->product->isMandatoryperiod() && $line->product->isService()) {
print 'jQuery("#date_end").addClass("error");';
}
}
}
print '</script>'
?>
@ -451,6 +468,15 @@ jQuery(document).ready(function()
}
});
$("#date_start, #date_end").focusout(function()
{
if ( $(this).val() == '' && !$(this).hasClass("error") ) {
$(this).addClass('error');
}else{
$(this).removeClass('error');
}
});
<?php
if (!empty($conf->margin->enabled)) {
?>

View File

@ -182,6 +182,22 @@ if (($line->info_bits & 2) == 2) {
if ($line->date_start || $line->date_end) {
print '<div class="clearboth nowraponall opacitymedium daterangeofline">'.get_date_range($line->date_start, $line->date_end, $format).'</div>';
}
if (!$line->date_start || !$line->date_end) {
// show warning under line
// we need to fetch product associated to line for some test
if ($object->element == 'propal' || $object->element == 'order' || $object->element == 'propal_supplier' || $object->element == 'supplier_proposal' || $object->element == 'commande') {
$res = $line->fetch_product();
if ($res > 0 ) {
if ($line->product->isService() && $line->product->isMandatoryPeriod()) {
print '<div><span class="clearboth nowraponall warning">'.$langs->trans("mandatoryPeriodNeedTobeSet").'</span></div>';
}
}
}
}
//print get_date_range($line->date_start, $line->date_end, $format);
}

View File

@ -57,6 +57,7 @@ INSERT INTO llx_c_email_templates (entity, module, type_template, lang, private,
-- v15
ALTER TABLE llx_product ADD COLUMN mandatory_period tinyint NULL DEFAULT 0;
ALTER TABLE llx_holiday ADD COLUMN date_approve DATETIME DEFAULT NULL;
ALTER TABLE llx_holiday ADD COLUMN fk_user_approve integer DEFAULT NULL;

View File

@ -103,6 +103,9 @@ create table llx_product
desiredstock float DEFAULT 0,
fk_unit integer DEFAULT NULL,
price_autogen tinyint DEFAULT 0,
fk_project integer DEFAULT NULL, -- Used when product was generated by a project or is specifif to a project
mandatory_period tinyint DEFAULT 0 -- is used to signal to the user that the start and end dates are mandatory for this type of product the fk_product_type == 1 (service) (non-blocking action)
fk_default_bom integer DEFAULT NULL,
fk_project integer DEFAULT NULL -- Used when the product was generated by a project or is specific to a project
)ENGINE=innodb;

View File

@ -401,5 +401,11 @@ DeleteLinkedProduct=Delete the child product linked to the combination
AmountUsedToUpdateWAP=Amount to use to update the Weighted Average Price
PMPValue=Weighted average price
PMPValueShort=WAP
mandatoryperiod=Mandatory periods
mandatoryPeriodNeedTobeSet=Attention periods not entered and mandatory
mandatoryPeriodNeedTobeSetMsgValidate=A service requires a start and end period
mandatoryHelper=Message to the user on the need to enter a start date and an end date on a service when creating / validating an invoice, commercial proposal, sales order. <br> This action is not blocking in the process of confirmation
DefaultBOM=Default BOM
DefaultBOMDesc=The default BOM recommended to use to manufacture this product. This field can be set only if nature of product is '%s'.
DefaultBOMDesc=The default BOM recommended to use to manufacture this product. This field can be set only if nature of product is '%s'.

View File

@ -399,3 +399,7 @@ ProductSupplierExtraFields=Attributs supplémentaires (Prix fournisseur)
DeleteLinkedProduct=Supprimer le produit enfant lié à la combinaison
PMPValue=Prix moyen pondéré (PMP)
PMPValueShort=PMP
mandatoryperiod=Périodes obligatoires
mandatoryPeriodNeedTobeSet=Attention périodes non saisies et obligatoires
mandatoryPeriodNeedTobeSetMsgValidate=Un service nécessite une période de début et de fin
mandatoryHelper=Message à l'utilisateur sur la necessité de saisir une date de début et une date de fin sur un service lors de la création/validation de facture, proposition commerciale , commande client.<br>Cette action n'est pas bloquante dans le processus de validation

View File

@ -91,7 +91,7 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) {
$outtype = $object->type;
$outqty = 1;
$outdiscount = 0;
$mandatory_period = $object->mandatory_period;
$found = false;
$price_level = 1;
@ -203,6 +203,7 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) {
'tva_tx' => $outtva_tx,
'qty' => $outqty,
'discount' => $outdiscount,
'mandatory_period' => $mandatory_period,
'array_options'=>$object->array_options);
}

View File

@ -105,6 +105,7 @@ $accountancy_code_buy = GETPOST('accountancy_code_buy', 'alpha');
$accountancy_code_buy_intra = GETPOST('accountancy_code_buy_intra', 'alpha');
$accountancy_code_buy_export = GETPOST('accountancy_code_buy_export', 'alpha');
$checkmandatory = GETPOST('accountancy_code_buy_export', 'alpha');
// by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
$label_security_check = empty($conf->global->MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML) ? 'alphanohtml' : 'restricthtml';
@ -298,7 +299,7 @@ if (empty($reshook)) {
$object->ref = $ref;
$object->label = GETPOST('label', $label_security_check);
$object->price_base_type = GETPOST('price_base_type', 'aZ09');
$object->mandatory_period = !empty(GETPOST("mandatoryperiod", 'alpha')) ? 1 : 0;
if ($object->price_base_type == 'TTC') {
$object->price_ttc = GETPOST('price');
} else {
@ -599,7 +600,7 @@ if (empty($reshook)) {
$accountancy_code_buy = GETPOST('accountancy_code_buy', 'alpha');
$accountancy_code_buy_intra = GETPOST('accountancy_code_buy_intra', 'alpha');
$accountancy_code_buy_export = GETPOST('accountancy_code_buy_export', 'alpha');
$checkmandatory = GETPOST('mandatoryperiod', 'alpha');
if (empty($accountancy_code_sell) || $accountancy_code_sell == '-1') {
$object->accountancy_code_sell = '';
} else {
@ -630,6 +631,11 @@ if (empty($reshook)) {
} else {
$object->accountancy_code_buy_export = $accountancy_code_buy_export;
}
if ($object->isService()) {
$object->mandatory_period = (!empty($checkmandatory)) ? 1 : 0 ;
}
// Fill array 'array_options' with data from add form
$ret = $extrafields->setOptionalsFromPost(null, $object);
@ -1446,6 +1452,14 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
print $form->load_tva("tva_tx", $defaultva, $mysoc, $mysoc, 0, 0, '', false, 1);
print '</td></tr>';
if (!empty($conf->service->enabled)) {
if ($object->isService()) {
// Mandatory period
print '<tr><td class="titlefieldcreate">'.$langs->trans("mandatoryperiod").'</td>';
print '<td><input type="checkbox" name="mandatoryperiod" /> ';
print '</td></tr>';
}
}
print '</table>';
print '<br>';
@ -2036,6 +2050,13 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
print '<td><input name="accountancy_code_buy_export" class="maxwidth200" value="'.$object->accountancy_code_buy_export.'">';
print '</td></tr>';
if ($object->isService()) {
// Mandatory period
print '<tr><td class="titlefieldcreate">'.$langs->trans("mandatoryperiod").'</td>';
print '<td><input type="checkbox" name="mandatoryperiod"'.($object->mandatory_period == 1 ? ' checked="checked"' : '').' /> ';
print '</td></tr>';
}
}
}
print '</table>';
@ -2310,6 +2331,13 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
print (!empty($object->duration_unit) && isset($dur[$object->duration_unit]) ? $langs->trans($dur[$object->duration_unit]) : '')."&nbsp;";
print '</td></tr>';
// Mandatory period
$htmltooltip = '<br>'.$langs->trans("mandatoryHelper");
print '<tr><td class="titlefield">'.$langs->trans("mandatoryperiod");
print $form->textwithpicto('', $htmltooltip, 1, 0).'</td><td>';
print '<input type="checkbox" name="mandatoryperiod"'.($object->mandatory_period == 1 ? ' checked="checked"' : '').' disabled/> ';
print '</td></tr>';
} else {
if (empty($conf->global->PRODUCT_DISABLE_NATURE)) {
// Nature

View File

@ -434,6 +434,13 @@ class Product extends CommonObject
public $is_object_used;
/**
*
*
*
*/
public $mandatory_period;
/**
* 'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
* Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
@ -484,6 +491,8 @@ class Product extends CommonObject
'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'index'=>0, 'position'=>1000),
//'tosell' =>array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'default'=>0, 'index'=>1, 'position'=>1000, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Active', -1=>'Cancel')),
//'tobuy' =>array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'default'=>0, 'index'=>1, 'position'=>1000, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Active', -1=>'Cancel')),
'mandatory_period' =>array('type'=>'integer', 'label'=>'mandatory_period', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'default'=>0, 'index'=>1, 'position'=>1000),
);
/**
@ -635,7 +644,7 @@ class Product extends CommonObject
// Barcode value
$this->barcode = trim($this->barcode);
$this->mandatory_period = empty($this->mandatory_period) ? 0 : $this->mandatory_period;
// Check parameters
if (empty($this->label)) {
$this->error = 'ErrorMandatoryParametersNotProvided';
@ -717,6 +726,7 @@ class Product extends CommonObject
$sql .= ", tobatch";
$sql .= ", batch_mask";
$sql .= ", fk_unit";
$sql .= ", mandatory_period";
$sql .= ") VALUES (";
$sql .= "'".$this->db->idate($now)."'";
$sql .= ", ".((int) $conf->entity);
@ -745,6 +755,7 @@ class Product extends CommonObject
$sql .= ", ".((empty($this->status_batch) || $this->status_batch < 0) ? '0' : ((int) $this->status_batch));
$sql .= ", '".$this->db->escape($this->batch_mask)."'";
$sql .= ", ".($this->fk_unit > 0 ? ((int) $this->fk_unit) : 'NULL');
$sql .= ", '".$this->db->escape($this->mandatory_period)."'";
$sql .= ")";
dol_syslog(get_class($this)."::Create", LOG_DEBUG);
@ -1149,7 +1160,7 @@ class Product extends CommonObject
$sql .= ", price_autogen = ".(!$this->price_autogen ? 0 : 1);
$sql .= ", fk_price_expression = ".($this->fk_price_expression != 0 ? (int) $this->fk_price_expression : 'NULL');
$sql .= ", fk_user_modif = ".($user->id > 0 ? $user->id : 'NULL');
$sql .= ", mandatory_period = ".($this->mandatory_period );
// stock field is not here because it is a denormalized value from product_stock.
$sql .= " WHERE rowid = ".((int) $id);
@ -2252,7 +2263,7 @@ class Product extends CommonObject
$sql .= " p.price_min, p.price_min_ttc, p.price_base_type, p.cost_price, p.default_vat_code, p.tva_tx, p.recuperableonly as tva_npr, p.localtax1_tx, p.localtax2_tx, p.localtax1_type, p.localtax2_type, p.tosell,";
$sql .= " p.tobuy, p.fk_product_type, p.duration, p.fk_default_warehouse, p.seuil_stock_alerte, p.canvas, p.net_measure, p.net_measure_units, p.weight, p.weight_units,";
$sql .= " p.length, p.length_units, p.width, p.width_units, p.height, p.height_units,";
$sql .= " p.surface, p.surface_units, p.volume, p.volume_units, p.barcode, p.fk_barcode_type, p.finished, p.fk_default_bom,";
$sql .= " p.surface, p.surface_units, p.volume, p.volume_units, p.barcode, p.fk_barcode_type, p.finished, p.fk_default_bom, p.mandatory_period,";
if (empty($conf->global->MAIN_PRODUCT_PERENTITY_SHARED)) {
$sql .= " p.accountancy_code_buy, p.accountancy_code_buy_intra, p.accountancy_code_buy_export, p.accountancy_code_sell, p.accountancy_code_sell_intra, p.accountancy_code_sell_export,";
} else {
@ -2427,6 +2438,8 @@ class Product extends CommonObject
$this->price_autogen = $obj->price_autogen;
$this->model_pdf = $obj->model_pdf;
$this->mandatory_period = $obj->mandatory_period;
$this->db->free($resql);
// fetch optionals attributes and labels
@ -5642,6 +5655,16 @@ class Product extends CommonObject
return ($this->type == Product::TYPE_SERVICE ? true : false);
}
/**
* Return if object have a constraint on mandatory_period
*
* @return boolean True if mandatory_period setted to 1
*/
public function isMandatoryPeriod()
{
return ($this->mandatory_period == 1 ? true : false);
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Get a barcode from the module to generate barcode values.