NEW Add a method doAutoRenewContracts that can be used as a cron task

This commit is contained in:
Laurent Destailleur 2022-08-23 11:49:49 +02:00
parent e9b370e7c0
commit 7b395f9f0f

View File

@ -2600,6 +2600,177 @@ class Contrat extends CommonObject
return -1;
}
}
/**
* Action executed by scheduler
* CAN BE A CRON TASK
* Loop on each contract lines and update the end of date. Do not execute the update if there is one pending invoice linked to contract.
*
* @param int $thirdparty_id Thirdparty id
* @param int $delayindaysshort To renew the resources x day before (positive value) or after (negative value) the end of date (default is 0)
* @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK)
*/
public function doAutoRenewContracts($thirdparty_id = 0, $delayindaysshort = 0)
{
global $langs, $user;
$langs->load("agenda");
$now = dol_now();
$enddatetoscan = dol_time_plus_duree($now, -1 * abs($delayindaysshort), 'd');
$error = 0;
$this->output = '';
$this->error='';
$contractlineprocessed = array();
$contractignored = array();
$contracterror = array();
dol_syslog(__METHOD__, LOG_DEBUG);
$sql = 'SELECT c.rowid, c.ref_customer, cd.rowid as lid, cd.date_fin_validite, p.duration';
$sql.= ' FROM '.MAIN_DB_PREFIX.'contrat as c, '.MAIN_DB_PREFIX.'contratdet as cd';
$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON p.rowid = cd.fk_product';
$sql.= ' WHERE cd.fk_contrat = c.rowid';
$sql.= " AND date_format(cd.date_fin_validite, '%Y-%m-%d') <= date_format('".$this->db->idate($enddatetoscan)."', '%Y-%m-%d')";
$sql.= " AND cd.statut = 4";
if ($thirdparty_id > 0) $sql.=" AND c.fk_soc = ".((int) $thirdparty_id);
//print $sql;
$resql = $this->db->query($sql);
if ($resql) {
$num = $this->db->num_rows($resql);
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
$i=0;
while ($i < $num) {
$obj = $this->db->fetch_object($resql);
if ($obj) {
if (! empty($contractlineprocessed[$obj->lid]) || ! empty($contractignored[$obj->rowid]) || ! empty($contracterror[$obj->rowid])) {
continue;
}
// Load contract
$object = new Contrat($this->db);
$object->fetch($obj->rowid); // fetch also lines
$object->fetch_thirdparty();
if ($object->id <= 0) {
$error++;
$this->errors[] = 'Failed to load contract with id='.$obj->rowid;
continue;
}
dol_syslog("* Process contract line in doRenewalContracts for contract id=".$object->id." ref=".$object->ref." ref_customer=".$object->ref_customer." contract line id=".$obj->lid);
// Update expiration date of line
$expirationdate = $this->db->jdate($obj->date_fin_validite);
$duration_value = preg_replace('/[^0-9]/', '', $obj->duration);
$duration_unit = preg_replace('/\d/', '', $obj->duration);
//var_dump($expirationdate.' '.$enddatetoscan);
// Test if there is pending invoice
$object->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 1);
if (is_array($object->linkedObjects['facture']) && count($object->linkedObjects['facture']) > 0) {
usort($object->linkedObjects['facture'], "cmp");
//dol_sort_array($contract->linkedObjects['facture'], 'date');
$someinvoicenotpaid=0;
foreach ($object->linkedObjects['facture'] as $idinvoice => $invoice) {
if ($invoice->statut == Facture::STATUS_DRAFT) continue; // Draft invoice are not invoice not paid
if (empty($invoice->paye)) {
$someinvoicenotpaid++;
}
}
if ($someinvoicenotpaid) {
$this->output .= 'Contract '.$object->ref.' is qualified for renewal but there is '.$someinvoicenotpaid.' invoice(s) unpayed so we cancel renewal'."\n";
$contractignored[$object->id]=$object->ref;
continue;
}
}
if ($expirationdate && $expirationdate < $enddatetoscan) {
dol_syslog("Define the newdate of end of services from expirationdate=".$expirationdate);
$newdate = $expirationdate;
$protecti=0; //$protecti is to avoid infinite loop
while ($newdate < $enddatetoscan && $protecti < 1000) {
$newdate = dol_time_plus_duree($newdate, $duration_value, $duration_unit);
$protecti++;
}
if ($protecti < 1000) { // If not, there is a pb
// We will update the end of date of contrat, so first we refresh contract data
dol_syslog("We will update the end of date of contract with newdate = ".dol_print_date($newdate, 'dayhourrfc'));
$this->db->begin();
$errorforlocaltransaction = 0;
$label = 'Renewal of contrat '.$object->ref.' line '.$obj->lid;
$comment = 'Renew date of contract '.$object->ref.' line '.$obj->lid.' by doAutoRenewContracts';
$sqlupdate = 'UPDATE '.MAIN_DB_PREFIX."contratdet SET date_fin_validite = '".$this->db->idate($newdate)."'";
$sqlupdate.= ' WHERE rowid = '.((int) $obj->lid);
$resqlupdate = $this->db->query($sqlupdate);
if ($resqlupdate) {
$contractlineprocessed[$obj->lid]=$object->ref;
$actioncode = 'RENEW_CONTRACT';
$now = dol_now();
// Create an event
$actioncomm = new ActionComm($this->db);
$actioncomm->type_code = 'AC_OTH_AUTO'; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
$actioncomm->code = 'AC_'.$actioncode;
$actioncomm->label = $label;
$actioncomm->datep = $now;
$actioncomm->datef = $now;
$actioncomm->percentage = -1; // Not applicable
$actioncomm->socid = $object->thirdparty->id;
$actioncomm->authorid = $user->id; // User saving action
$actioncomm->userownerid = $user->id; // Owner of action
$actioncomm->fk_element = $object->id;
$actioncomm->elementtype = 'contract';
$actioncomm->note_private = $comment;
$ret = $actioncomm->create($user); // User creating action
} else {
$contracterror[$object->id]=$object->ref;
$error++;
$errorforlocaltransaction++;
$this->error = $this->db->lasterror();
}
if (! $errorforlocaltransaction) {
$this->db->commit();
} else {
$this->db->rollback();
}
} else {
$error++;
$this->error = "Bad value for newdate in doAutoRenewContracts - expirationdate=".$expirationdate." enddatetoscan=".$enddatetoscan." duration_value=".$duration_value." duration_unit=".$duration_value;
dol_syslog($this->error, LOG_ERR);
}
}
}
$i++;
}
} else {
$error++;
$this->error = $this->db->lasterror();
}
$this->output .= count($contractlineprocessed).' contract line(s) with end date before '.dol_print_date($enddatetoscan, 'day').' were renewed'.(count($contractlineprocessed)>0 ? ' : '.join(',', $contractlineprocessed) : '');
return ($error ? 1: 0);
}
}