diff --git a/htdocs/compta/facture/class/facture-rec.class.php b/htdocs/compta/facture/class/facture-rec.class.php index f2ef90677c5..91e3388c8ff 100644 --- a/htdocs/compta/facture/class/facture-rec.class.php +++ b/htdocs/compta/facture/class/facture-rec.class.php @@ -30,6 +30,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; /** @@ -83,7 +84,20 @@ class FactureRec extends Facture // Clean parameters $this->titre=trim($this->titre); $this->usenewprice=empty($this->usenewprice)?0:$this->usenewprice; - + + // No frequency defined then no next date to execution + if (empty($this->frequency)) + { + $this->frequency=0; + $this->date_when=NULL; + } + + + $this->frequency=abs($this->frequency); + $this->nb_gen_done=0; + $this->nb_gen_max=empty($this->nb_gen_max)?0:$this->nb_gen_max; + $this->auto_validate=empty($this->auto_validate)?0:$this->auto_validate; + $this->db->begin(); // Charge facture modele @@ -108,6 +122,13 @@ class FactureRec extends Facture $sql.= ", fk_cond_reglement"; $sql.= ", fk_mode_reglement"; $sql.= ", usenewprice"; + $sql.= ", frequency"; + $sql.= ", unit_frequency"; + $sql.= ", date_when"; + $sql.= ", date_last_gen"; + $sql.= ", nb_gen_done"; + $sql.= ", nb_gen_max"; + $sql.= ", auto_validate"; $sql.= ") VALUES ("; $sql.= "'".$this->titre."'"; $sql.= ", ".$facsrc->socid; @@ -122,6 +143,13 @@ class FactureRec extends Facture $sql.= ", '".$facsrc->cond_reglement_id."'"; $sql.= ", '".$facsrc->mode_reglement_id."'"; $sql.= ", ".$this->usenewprice; + $sql.= ", ".$this->frequency; + $sql.= ", '".$this->db->escape($this->unit_frequency)."'"; + $sql.= ", ".(!empty($this->date_when)?"'".$this->db->idate($this->date_when)."'":'NULL'); + $sql.= ", ".(!empty($this->date_last_gen)?"'".$this->db->idate($this->date_last_gen)."'":'NULL'); + $sql.= ", ".$this->nb_gen_done; + $sql.= ", ".$this->nb_gen_max; + $sql.= ", ".$this->auto_validate; $sql.= ")"; if ($this->db->query($sql)) @@ -198,6 +226,7 @@ class FactureRec extends Facture $sql.= ', f.date_lim_reglement as dlr'; $sql.= ', f.note_private, f.note_public, f.fk_user_author'; $sql.= ', f.fk_mode_reglement, f.fk_cond_reglement'; + $sql.= ', f.frequency, f.unit_frequency, f.date_when, f.date_last_gen, f.nb_gen_done, f.nb_gen_max, f.usenewprice, f.auto_validate'; $sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle'; $sql.= ', c.code as cond_reglement_code, c.libelle as cond_reglement_libelle, c.libelle_facture as cond_reglement_libelle_doc'; $sql.= ', el.fk_source'; @@ -255,6 +284,14 @@ class FactureRec extends Facture $this->modelpdf = $obj->model_pdf; $this->rang = $obj->rang; $this->special_code = $obj->special_code; + $this->frequency = $obj->frequency; + $this->unit_frequency = $obj->unit_frequency; + $this->date_when = $obj->date_when; + $this->date_last_gen = $obj->date_last_gen; + $this->nb_gen_done = $obj->nb_gen_done; + $this->nb_gen_max = $obj->nb_gen_max; + $this->usenewprice = $obj->usenewprice; + $this->auto_validate = $obj->auto_validate; if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1; @@ -468,7 +505,7 @@ class FactureRec extends Facture $total_ht = $tabprice[0]; $total_tva = $tabprice[1]; $total_ttc = $tabprice[2]; - + $product_type=$type; if ($fk_product) { @@ -529,6 +566,58 @@ class FactureRec extends Facture } } + /** + * Return the next date of + * + * @return timestamp false if KO, timestamp if OK + */ + function getNextDate() + { + if (empty($this->date_when)) return false; + return dol_time_plus_duree($this->date_when, $this->frequency, $this->unit_frequency); + } + + /** + * Create all recurrents invoices + * + * @return int number of created invoices + */ + function createRecurringInvoices() + { + global $db,$user; + + $nb_create=0; + + $today = date('Y-m-d 23:59:59'); + + $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'facture_rec'; + $sql.= ' WHERE date_when IS NOT NULL'; + $sql.= ' AND date_when <= "'.$db->escape($today).'"'; + $sql.= ' AND nb_gen_done < nb_gen_max'; + + $resql = $db->query($sql); + if ($resql) + { + while ($line = $db->fetch_object($resql)) + { + $facturerec = new FactureRec($db); + $facturerec->fetch($line->rowid); + + $facture = new Facture($db); + + $result = $facture->createFromRec($user, $facturerec); + + // >0 create and validate if auto_validate + // =0 create but not validate if auto_validate + // <0 broken + if ($result >= 0) $nb_create++; + + } + } + + return $nb_create; + } + /** * Return clicable name (with picto eventually) * diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index fe38c17ff0d..edb22982843 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -917,6 +917,66 @@ class Facture extends CommonInvoice else return -1; } + /** + * Create a new invoice in database from facturerec + * + * @param User $user Object user that ask creation + * @return int <0 if KO, 0 if not validate, >0 if OK + */ + function createFromRec($user, $object) + { + + // Clean parameters + if (! $this->type) $this->type = self::TYPE_STANDARD; + $this->ref_client=trim($this->ref_client); + $this->note_private=trim($this->note_private); + $this->note_public=trim($this->note_public); + //if (! $this->remise) $this->remise = 0; + if (! $this->mode_reglement_id) $this->mode_reglement_id = 0; + $this->brouillon = 1; + + // Charge facture source + $facture=new Facture($object->db); + + $facture->fk_facture_source = $object->fk_facture_source; + $facture->type = $object->type; + $facture->socid = $object->socid; + $facture->date = dol_mktime(12, 0, 0, date('m'), date('d'), date('Y')); + $facture->note_public = $object->note_public; + $facture->note_private = $object->note_private; + $facture->ref_client = $object->ref_client; + $facture->modelpdf = $object->modelpdf; + $facture->fk_project = $object->fk_project; + $facture->cond_reglement_id = $object->cond_reglement_id; + $facture->mode_reglement_id = $object->mode_reglement_id; + $facture->remise_absolue = $object->remise_absolue; + $facture->remise_percent = $object->remise_percent; + $facture->fk_incoterms = $object->fk_incoterms; + $facture->location_incoterms= $object->location_incoterms; + + $facture->lines = $object->lines; // Tableau des lignes de factures + $facture->products = $object->lines; // Tant que products encore utilise + + // Loop on each line of new invoice + foreach($facture->lines as $i => $line) + { + $facture->lines[$i]->fk_prev_id = $object->lines[$i]->rowid; + } + + dol_syslog(get_class($this)."::createFromRec socid=".$this->socid." nboflines=".count($facture->lines)); + + $facid = $facture->create($user); + if ($facid <= 0) return -1; + + if ($object->auto_validate) + { + $result = $facture->validate($user); + if ($result<=0) return 0; + } + + return $facid; + } + /** * Return clicable link of object (with eventually picto) * diff --git a/htdocs/compta/facture/fiche-rec.php b/htdocs/compta/facture/fiche-rec.php index 5f17c5d49bb..b9c3b58a224 100644 --- a/htdocs/compta/facture/fiche-rec.php +++ b/htdocs/compta/facture/fiche-rec.php @@ -75,12 +75,42 @@ if ($action == 'add') $error++; } + $frequency=GETPOST('frequency', 'int'); + $reyear=GETPOST('reyear'); + $remonth=GETPOST('remonth'); + $reday=GETPOST('reday'); + $nb_gen_max=GETPOST('nb_gen_max', 'int'); + + if (GETPOST('frequency')) + { + if (empty($reyear) || empty($remonth) || empty($reday)) + { + setEventMessages($langs->transnoentities("ErrorFieldRequired",$langs->trans("Date")), null, 'errors'); + $action = "create"; + $error++; + } + if (empty($nb_gen_max)) + { + setEventMessages($langs->transnoentities("ErrorFieldRequired",$langs->trans("MaxPeriodNumber")), null, 'errors'); + $action = "create"; + $error++; + } + } + if (! $error) { $object->titre = GETPOST('titre', 'alpha'); $object->note_private = GETPOST('note_private'); $object->usenewprice = GETPOST('usenewprice'); - + + $object->frequency = $frequency; + $object->unit_frequency = GETPOST('unit_frequency', 'alpha'); + $object->nb_gen_max = $nb_gen_max; + $object->auto_validate = GETPOST('auto_validate', 'int'); + + $date_next_execution = dol_mktime(12, 0, 0, $remonth, $reday, $reyear); + $object->date_when = $date_next_execution; + if ($object->create($user, $id) > 0) { $id = $object->id; @@ -185,6 +215,38 @@ if ($action == 'create') print '
'; + + // Recurrence + $title = $langs->trans("Recurrence"); + print load_fiche_titre($title); + + print ''; + + // Frequency + print ""; + + // First date of execution for cron + print ""; + + // Number max of generation + print ""; + + // Auto validate the invoice + print ""; + + print "
".$form->textwithpicto($langs->trans("Frequency"), $langs->transnoentitiesnoconv('toolTipFrequency')).""; + print " ".$form->selectarray('unit_frequency', array('d'=>$langs->trans('Day'), 'm'=>$langs->trans('Month'), 'y'=>$langs->trans('Year')), (GETPOST('unit_frequency')?GETPOST('unit_frequency'):'m')); + print "
".$langs->trans('NextDateToExecution').""; + $date_next_execution = isset($date_next_execution) ? $date_next_execution : dol_mktime(12, 0, 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')); + print $form->select_date($date_next_execution, '', 0, 0, '', "add", 1, 1, 1); + print "
".$langs->trans("MaxPeriodNumber").""; + print ""; + print "
".$langs->trans("InvoiceAutoValidate").""; + print $form->selectyesno('auto_validate', 0, 1); + print "
"; + + print '
'; + $title = $langs->trans("ProductsAndServices"); if (empty($conf->service->enabled)) $title = $langs->trans("Products"); @@ -446,6 +508,34 @@ else print ''; + /* + * Recurrence + */ + if ($object->frequency > 0) + { + $title = $langs->trans("Recurrence"); + print load_fiche_titre($title); + + print '
'; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print '
'.$langs->trans("Frequency").''.$langs->trans('FrequencyPer_'.$object->unit_frequency, $object->frequency).'
'.$langs->trans("NextDateToExecution").''.dol_print_date($object->date_when, 'daytext').'
'.$langs->trans("MaxPeriodNumber").' / '.$langs->trans("RestPeriodNumber").''.$object->nb_gen_max.' / '.($object->nb_gen_max-$object->nb_gen_done).'
'.$langs->trans("InvoiceAutoValidate").''.yn($object->auto_validate).'
'; + print '
'; + print '
'; + } + /* * Lines */ diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index f7cb1ff7881..f17be59d0db 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -2322,7 +2322,7 @@ abstract class CommonObject { $error++; $this->error=$this->db->lasterror(); - $this->error[]=$this->db->lasterror(); + $this->errors[]=$this->db->lasterror(); } } diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index 50e0c4d2e56..57f3c10ebe4 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -211,4 +211,19 @@ ALTER TABLE llx_propaldet ADD COLUMN multicurrency_total_ht double(24,8) DEFAULT ALTER TABLE llx_propaldet ADD COLUMN multicurrency_total_tva double(24,8) DEFAULT 0; ALTER TABLE llx_propaldet ADD COLUMN multicurrency_total_ttc double(24,8) DEFAULT 0; - \ No newline at end of file + +ALTER TABLE llx_facture_rec ADD COLUMN auto_validate integer DEFAULT 0; + +ALTER TABLE llx_facture_rec ADD COLUMN fk_multicurrency integer; +ALTER TABLE llx_facture_rec ADD COLUMN multicurrency_code varchar(255); +ALTER TABLE llx_facture_rec ADD COLUMN multicurrency_tx double(24,8) DEFAULT 1; +ALTER TABLE llx_facture_rec ADD COLUMN multicurrency_total_ht double(24,8) DEFAULT 0; +ALTER TABLE llx_facture_rec ADD COLUMN multicurrency_total_tva double(24,8) DEFAULT 0; +ALTER TABLE llx_facture_rec ADD COLUMN multicurrency_total_ttc double(24,8) DEFAULT 0; + +ALTER TABLE llx_facturedet_rec ADD COLUMN fk_multicurrency integer; +ALTER TABLE llx_facturedet_rec ADD COLUMN multicurrency_code varchar(255); +ALTER TABLE llx_facturedet_rec ADD COLUMN multicurrency_subprice double(24,8) DEFAULT 0; +ALTER TABLE llx_facturedet_rec ADD COLUMN multicurrency_total_ht double(24,8) DEFAULT 0; +ALTER TABLE llx_facturedet_rec ADD COLUMN multicurrency_total_tva double(24,8) DEFAULT 0; +ALTER TABLE llx_facturedet_rec ADD COLUMN multicurrency_total_ttc double(24,8) DEFAULT 0; diff --git a/htdocs/install/mysql/tables/llx_facture_rec.sql b/htdocs/install/mysql/tables/llx_facture_rec.sql index 7c1151f36ca..19fdb14516b 100644 --- a/htdocs/install/mysql/tables/llx_facture_rec.sql +++ b/htdocs/install/mysql/tables/llx_facture_rec.sql @@ -50,6 +50,13 @@ create table llx_facture_rec note_private text, note_public text, + fk_multicurrency integer, + multicurrency_code varchar(255), + multicurrency_tx double(24,8) DEFAULT 1, + multicurrency_total_ht double(24,8) DEFAULT 0, + multicurrency_total_tva double(24,8) DEFAULT 0, + multicurrency_total_ttc double(24,8) DEFAULT 0, + usenewprice integer DEFAULT 0, -- update invoice with current price of product instead of recorded price frequency integer, -- frequency (for example: 3 for every 3 month) unit_frequency varchar(2) DEFAULT 'm', -- 'm' for month (date_when must be a day <= 28), 'y' for year, ... @@ -57,5 +64,6 @@ create table llx_facture_rec date_when datetime DEFAULT NULL, -- date for next gen (when an invoice is generated, this field must be updated with next date) date_last_gen datetime DEFAULT NULL, -- date for last gen (date with last successfull generation of invoice) nb_gen_done integer DEFAULT NULL, -- nb of generation done (when an invoice is generated, this field must incremented) - nb_gen_max integer DEFAULT NULL -- maximum number of generation + nb_gen_max integer DEFAULT NULL, -- maximum number of generation + auto_validate integer DEFAULT 0 -- 0 to create in draft, 1 to create and validate the new invoice )ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_facturedet_rec.sql b/htdocs/install/mysql/tables/llx_facturedet_rec.sql index 164dc2f94d5..e0e7358af0c 100644 --- a/htdocs/install/mysql/tables/llx_facturedet_rec.sql +++ b/htdocs/install/mysql/tables/llx_facturedet_rec.sql @@ -48,5 +48,12 @@ create table llx_facturedet_rec special_code integer UNSIGNED DEFAULT 0, -- code pour les lignes speciales rang integer DEFAULT 0, -- ordre d'affichage fk_contract_line integer NULL, -- id of contract line when predefined invoice comes from contract lines - fk_unit integer DEFAULT NULL + fk_unit integer DEFAULT NULL, + + fk_multicurrency integer, + multicurrency_code varchar(255), + multicurrency_subprice double(24,8) DEFAULT 0, + multicurrency_total_ht double(24,8) DEFAULT 0, + multicurrency_total_tva double(24,8) DEFAULT 0, + multicurrency_total_ttc double(24,8) DEFAULT 0 )ENGINE=innodb; diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index 23f9ba4b532..754ddc1547c 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -305,7 +305,13 @@ AmountPaymentDistributedOnInvoice=Payment amount distributed on invoice PaymentNote=Payment note ListOfPreviousSituationInvoices=List of previous situation invoices ListOfNextSituationInvoices=List of next situation invoices - +FrequencyPer_d=Every %s days +FrequencyPer_m=Every %s months +FrequencyPer_y=Every %s years +toolTipFrequency=Examples:
Set 7 / day: give a new invoice every 7 days
Set 3 / month: give a new invoice every 3 month +NextDateToExecution=Next date to execution +MaxPeriodNumber=Max period number +InvoiceAutoValidate=Automatically validate invoice # PaymentConditions PaymentConditionShortRECEP=Immediate PaymentConditionRECEP=Immediate