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 "| ".$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 " |
";
+
+ // First date of execution for cron
+ 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 " |
";
+
+ // Number max of generation
+ print "| ".$langs->trans("MaxPeriodNumber")." | ";
+ print "";
+ print " |
";
+
+ // Auto validate the invoice
+ print "| ".$langs->trans("InvoiceAutoValidate")." | ";
+ print $form->selectyesno('auto_validate', 0, 1);
+ print " |
";
+
+ 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 '| '.$langs->trans("Frequency").' | ';
+ print ''.$langs->trans('FrequencyPer_'.$object->unit_frequency, $object->frequency).' | ';
+
+ print '
| '.$langs->trans("NextDateToExecution").' | ';
+ print ''.dol_print_date($object->date_when, 'daytext').' | ';
+
+ print '
| '.$langs->trans("MaxPeriodNumber").' / '.$langs->trans("RestPeriodNumber").' | ';
+ print ''.$object->nb_gen_max.' / '.($object->nb_gen_max-$object->nb_gen_done).' | ';
+
+ print '
| '.$langs->trans("InvoiceAutoValidate").' | ';
+ print ''.yn($object->auto_validate).' | ';
+
+ print '
';
+ 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