diff --git a/ChangeLog b/ChangeLog index f61d3a4820d..e4fbf63e758 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,8 +8,9 @@ For Users: NEW: Stable module: Website NEW: Stable module: WebDAV NEW: Stable module: Module Builder -NEW: Stable module "Skype" has been replaced with module "Social Networks" to support more tools. +NEW: Stable module "Skype" has been replaced with module "Social Networks" to support more services. NEW: Experimental module "TakePos" +NEW: Experimental module "Ticket" NEW: Dolibarr can provide information in page title when multicompany is enabled of not, making Android application like DoliDroid able to provide native features for multicompany module. NEW: Compatibility with PHP 7.3 @@ -22,11 +23,92 @@ For developers: WARNING: Following changes may create regressions for some external modules, but were necessary to make Dolibarr better: -* If you use some links like viewimages.php?modulepart=mycompany&file=... in you external modules, you must +* If you use some links like viewimages.php?modulepart=mycompany&file=... in your external modules, you must replace them with links like viewimages.php?modulepart=mycompany&file=logos/... (note that link change only for modulepart=mycompany that now works like others). +* Hidden option MAIN_PDF_SHIPPING_DISPLAY_AMOUNT_HT has been renamed into SHIPPING_PDF_DISPLAY_AMOUNT_HT +***** ChangeLog for 8.0.3 compared to 8.0.2 ***** +FIX: #9161 +FIX: #9432 +FIX: #9432 Assign yourself as a commercial when you don't have permission to see all thirds +FIX: #9510 +FIX: #9567 +FIX: According to french law, if seller is in France and buyer isn't in UE and isn't a company, TVA used = TVA product +FIX: Amount when using mutlicurrency on PDF +FIX: Backup of database without mysqladmin available from cron. +FIX: Bad label on delete button +FIX: bad link in notification +FIX: Bad position of hook formattachOptions call +FIX: Can't create shipping if have shipping line's extrafields +FIX: check !empty exclude select element +FIX: content lost when editing a label with " +FIX: correct migration of old postgresql unique key +FIX: credit note progression +FIX: default accounting accounts on loan creation #9643 +FIX: Delete of draft invoice +FIX: deletion on draft is allowed if we are allwoed to create +FIX: Do not show check box if not applicable +FIX: exclude element of the select +FIX: extrafields of taks not visible in creation +FIX: filter on employee +FIX: invoice stats: situation invoices were not counted +FIX: keep external module element when adding resource +FIX: langs fr +FIX: Link template invoice to contract +FIX: Look and feel v8. Missing button "Create category" +FIX: Menu to show/edit Users categories was missing +FIX: missing name alias field in societe import/export #9091 +FIX: missing symbol for indian rupies +FIX: Missing transaction around action +FIX: modify parenting before task deletion +FIX: nb of session in title +FIX: need to filter on current entity on replenish +FIX: number mailing for a contact with multicompany +FIX: Option for prof id mandatory not working with custom type of company +FIX: Option MAIN_DISABLE_NOTES_TAB #9611 +FIX: Pagination stats +FIX: pdf typhon: order reference duplicate +FIX: position 0 for emails templates +FIX: previous situation invoice selection +FIX: Product marge tabs on product card +FIX: Product margin tab and credit note +FIX: propal pdf: missing parenthesis for customs code +FIX: properties on proposal must not be modified if error +FIX: qty not visible for a lot when making shipment on a dedicated stock +FIX: Quick hack to solve pb of bad definition of public holidays +FIX: remain to pay for credit note was wrong on invoice list +FIX: replenish wasn't caring about supplier price min quantity #9561 +FIX: Required extrafield value numeric should accept '0' +FIX: ressource list with extrafields +FIX: restore last seach criteria +FIX: Selection of addmaindocfile is lost on error +FIX: Sending of reminder for expired subscriptions +FIX: shared link ko on proposals +FIX: showOptionals: column mismatches +FIX: situation invoice total with credit note +FIX: situation invoice prev percent +FIX: special code on create supplier invoice from supplier order +FIX: Symbol of currency in substitution variables +FIX: The max size for upload file was not corectly shown +FIX: the member e-mail on resign and validation. +FIX: thirdparty property of object not loaded when only one record +FIX: title +FIX: Title problem on admin RSS module +FIX: Tooltip on invoice widget +FIX: Total of timespent +FIX: trackid into email sent from member module. +FIX: translation in select unit form +FIX: use discount with multicurrency +FIX: Variable name +FIX: When we delete a product, llx_product_association rows are not deleted +FIX: when we're just admin and not super admin, if we create new user with transverse mode, we don't see it then we can't add him in usergroup +FIX: wrong function name +FIX: wrong occurence number of contract on contact card, we must only count externals +FIX: wrong value for module part and return access denied +FIX: Wrong variable name +FIX: XSS vulnerability reported by Mary Princy E ***** ChangeLog for 8.0.2 compared to 8.0.1 ***** FIX: #8452 diff --git a/build/makepack-dolibarr.pl b/build/makepack-dolibarr.pl index 01e282aa492..179f5dcd447 100755 --- a/build/makepack-dolibarr.pl +++ b/build/makepack-dolibarr.pl @@ -522,10 +522,17 @@ if ($nboftargetok) { $ret=`rm -f $BUILDROOT/$PROJECT/doc/images/dolibarr_screenshot12.png`; # Security to avoid to package data files + print "Remove documents dir\n"; $ret=`rm -fr $BUILDROOT/$PROJECT/document`; $ret=`rm -fr $BUILDROOT/$PROJECT/documents`; $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/document`; $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/documents`; + + print "Remove subdir of custom dir\n"; + print "find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \\;\n"; + $ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \\; >/dev/null 2>&1`; # For custom we want to remove all subdirs but not files + print "find $BUILDROOT/$PROJECT/htdocs/custom/* -type l -exec rm -fr {} \\;\n"; + $ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type l -exec rm -fr {} \\; >/dev/null 2>&1`; # For custom we want to remove all subdirs, even symbolic links, but not files # Removed known external modules to avoid any error when packaging from env where external modules are tested $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/allscreens*`; @@ -591,13 +598,6 @@ if ($nboftargetok) { $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/tecnickcom/tcpdf/tools`; $ret=`rm -f $BUILDROOT/$PROJECT/htdocs/includes/tecnickcom/tcpdf/LICENSE.TXT`; $ret=`rm -f $BUILDROOT/$PROJECT/htdocs/theme/common/octicons/LICENSE`; - - - print "Remove subdir of custom dir\n"; - print "find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \\;\n"; - $ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \\; >/dev/null 2>&1`; # For custom we want to remove all subdirs but not files - print "find $BUILDROOT/$PROJECT/htdocs/custom/* -type l -exec rm -fr {} \\;\n"; - $ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type l -exec rm -fr {} \\; >/dev/null 2>&1`; # For custom we want to remove all subdirs, even symbolic links, but not files } # Build package for each target diff --git a/htdocs/accountancy/admin/accountmodel.php b/htdocs/accountancy/admin/accountmodel.php index db21cd62192..e8f6b8e43b7 100644 --- a/htdocs/accountancy/admin/accountmodel.php +++ b/htdocs/accountancy/admin/accountmodel.php @@ -44,7 +44,7 @@ if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT . '/core // Load translation files required by the page $langs->loadLangs(array("errors","admin","companies","resource","holiday","compta","accountancy","hrm")); -$action=GETPOST('action','alpha')?GETPOST('action','alpha'):'view'; +$action=GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view'; $confirm=GETPOST('confirm','alpha'); $id=31; $rowid=GETPOST('rowid','alpha'); diff --git a/htdocs/accountancy/admin/categories_list.php b/htdocs/accountancy/admin/categories_list.php index 9f6b0bf14fe..1f10403f798 100644 --- a/htdocs/accountancy/admin/categories_list.php +++ b/htdocs/accountancy/admin/categories_list.php @@ -34,7 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php'; // Load translation files required by the page $langs->loadLangs(array("errors","admin","companies","resource","holiday","accountancy","hrm")); -$action=GETPOST('action','alpha')?GETPOST('action','alpha'):'view'; +$action=GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view'; $confirm=GETPOST('confirm','alpha'); $id=32; $rowid=GETPOST('rowid','alpha'); diff --git a/htdocs/accountancy/admin/defaultaccounts.php b/htdocs/accountancy/admin/defaultaccounts.php index 31fc04fa745..6de48b58cd5 100644 --- a/htdocs/accountancy/admin/defaultaccounts.php +++ b/htdocs/accountancy/admin/defaultaccounts.php @@ -43,7 +43,7 @@ if (empty($user->rights->accounting->chartofaccount)) accessforbidden(); } -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'aZ09'); $list_account_main = array ( diff --git a/htdocs/accountancy/admin/export.php b/htdocs/accountancy/admin/export.php index eccb9b0c06f..03538b759e7 100644 --- a/htdocs/accountancy/admin/export.php +++ b/htdocs/accountancy/admin/export.php @@ -41,7 +41,7 @@ if (empty($user->rights->accounting->chartofaccount)) accessforbidden(); } -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'aZ09'); // Parameters ACCOUNTING_EXPORT_* $main_option = array ( diff --git a/htdocs/accountancy/admin/fiscalyear_card.php b/htdocs/accountancy/admin/fiscalyear_card.php index 05c7a3fd174..297a3026b02 100644 --- a/htdocs/accountancy/admin/fiscalyear_card.php +++ b/htdocs/accountancy/admin/fiscalyear_card.php @@ -38,7 +38,7 @@ if (empty($user->rights->accounting->fiscalyear)) $error = 0; -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'aZ09'); $confirm = GETPOST('confirm', 'alpha'); $id = GETPOST('id', 'int'); diff --git a/htdocs/accountancy/admin/index.php b/htdocs/accountancy/admin/index.php index a17ee45ae4d..60a81df9a56 100644 --- a/htdocs/accountancy/admin/index.php +++ b/htdocs/accountancy/admin/index.php @@ -41,7 +41,7 @@ if (empty($user->rights->accounting->chartofaccount)) accessforbidden(); } -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'aZ09'); // Parameters ACCOUNTING_* and others $list = array ( diff --git a/htdocs/accountancy/admin/journals_list.php b/htdocs/accountancy/admin/journals_list.php index 3511ae431ec..49745676eb3 100644 --- a/htdocs/accountancy/admin/journals_list.php +++ b/htdocs/accountancy/admin/journals_list.php @@ -34,7 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php'; // Load translation files required by the page $langs->loadLangs(array("admin","compta","accountancy")); -$action=GETPOST('action','alpha')?GETPOST('action','alpha'):'view'; +$action=GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view'; $confirm=GETPOST('confirm','alpha'); $id=35; $rowid=GETPOST('rowid','alpha'); diff --git a/htdocs/accountancy/admin/productaccount.php b/htdocs/accountancy/admin/productaccount.php index a84e2f6fcab..246d3752f56 100644 --- a/htdocs/accountancy/admin/productaccount.php +++ b/htdocs/accountancy/admin/productaccount.php @@ -45,7 +45,7 @@ if (! $user->rights->accounting->bind->write) accessforbidden(); // search & action GETPOST -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'aZ09'); $codeventil_buy = GETPOST('codeventil_buy', 'array'); $codeventil_sell = GETPOST('codeventil_sell', 'array'); $chk_prod = GETPOST('chk_prod', 'array'); diff --git a/htdocs/accountancy/bookkeeping/balance.php b/htdocs/accountancy/bookkeeping/balance.php index 9feb2a7c234..e807d1acced 100644 --- a/htdocs/accountancy/bookkeeping/balance.php +++ b/htdocs/accountancy/bookkeeping/balance.php @@ -41,7 +41,7 @@ $langs->loadLangs(array("accountancy")); $page = GETPOST("page"); $sortorder = GETPOST("sortorder", 'alpha'); $sortfield = GETPOST("sortfield", 'alpha'); -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'aZ09'); if (GETPOST("exportcsv",'alpha')) $action = 'export_csv'; // Load variable for pagination @@ -151,6 +151,7 @@ if ($action == 'export_csv') $sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV; $filename = 'balance'; + $type_export = 'balance'; include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php'; $result = $object->fetchAllBalance($sortorder, $sortfield, 0, 0, $filter); diff --git a/htdocs/accountancy/bookkeeping/list.php b/htdocs/accountancy/bookkeeping/list.php index 46575c21143..5613e42b4c4 100644 --- a/htdocs/accountancy/bookkeeping/list.php +++ b/htdocs/accountancy/bookkeeping/list.php @@ -36,10 +36,10 @@ require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php'; // Load translation files required by the page $langs->loadLangs(array("accountancy")); -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'aZ09'); $search_mvt_num = GETPOST('search_mvt_num', 'int'); -$search_doc_type = GETPOST("search_doc_type"); -$search_doc_ref = GETPOST("search_doc_ref"); +$search_doc_type = GETPOST("search_doc_type", 'alpha'); +$search_doc_ref = GETPOST("search_doc_ref", 'alpha'); $search_date_start = dol_mktime(0, 0, 0, GETPOST('search_date_startmonth', 'int'), GETPOST('search_date_startday', 'int'), GETPOST('search_date_startyear', 'int')); $search_date_end = dol_mktime(0, 0, 0, GETPOST('search_date_endmonth', 'int'), GETPOST('search_date_endday', 'int'), GETPOST('search_date_endyear', 'int')); $search_doc_date = dol_mktime(0, 0, 0, GETPOST('doc_datemonth', 'int'), GETPOST('doc_dateday', 'int'), GETPOST('doc_dateyear', 'int')); diff --git a/htdocs/accountancy/class/accountancyexport.class.php b/htdocs/accountancy/class/accountancyexport.class.php index 7b832976e89..5d76a7155d6 100644 --- a/htdocs/accountancy/class/accountancyexport.class.php +++ b/htdocs/accountancy/class/accountancyexport.class.php @@ -218,6 +218,7 @@ class AccountancyExport // Define name of file to save $filename = 'general_ledger-'.$this->getFormatCode($conf->global->ACCOUNTING_EXPORT_MODELCSV); + $type_export = 'general_ledger'; include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php'; diff --git a/htdocs/accountancy/journal/bankjournal.php b/htdocs/accountancy/journal/bankjournal.php index 4b8c4ce3c22..4c0bf9a55fa 100644 --- a/htdocs/accountancy/journal/bankjournal.php +++ b/htdocs/accountancy/journal/bankjournal.php @@ -750,6 +750,7 @@ if ($action == 'exportcsv') { // ISO and not UTF8 ! $sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV; $filename = 'journal'; + $type_export = 'journal'; include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php'; // CSV header line diff --git a/htdocs/accountancy/journal/expensereportsjournal.php b/htdocs/accountancy/journal/expensereportsjournal.php index 197d18f2380..a767d469120 100644 --- a/htdocs/accountancy/journal/expensereportsjournal.php +++ b/htdocs/accountancy/journal/expensereportsjournal.php @@ -429,6 +429,7 @@ $userstatic = new User($db); $sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV; $filename = 'journal'; + $type_export = 'journal'; include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php'; // Model Cegid Expert Export diff --git a/htdocs/accountancy/journal/purchasesjournal.php b/htdocs/accountancy/journal/purchasesjournal.php index 7984443d906..1b71b288c6b 100644 --- a/htdocs/accountancy/journal/purchasesjournal.php +++ b/htdocs/accountancy/journal/purchasesjournal.php @@ -586,6 +586,7 @@ if ($action == 'exportcsv') { // ISO and not UTF8 ! $sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV; $filename = 'journal'; + $type_export = 'journal'; include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php'; $companystatic = new Fournisseur($db); diff --git a/htdocs/accountancy/journal/sellsjournal.php b/htdocs/accountancy/journal/sellsjournal.php index f3a3246c1ea..21736195073 100644 --- a/htdocs/accountancy/journal/sellsjournal.php +++ b/htdocs/accountancy/journal/sellsjournal.php @@ -547,6 +547,7 @@ if ($action == 'exportcsv') { // ISO and not UTF8 ! $sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV; $filename = 'journal'; + $type_export = 'journal'; include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php'; $companystatic = new Client($db); diff --git a/htdocs/accountancy/tpl/export_journal.tpl.php b/htdocs/accountancy/tpl/export_journal.tpl.php index d0d902770c0..ca3c9c49ea0 100644 --- a/htdocs/accountancy/tpl/export_journal.tpl.php +++ b/htdocs/accountancy/tpl/export_journal.tpl.php @@ -35,9 +35,8 @@ $endaccountingperiod = dol_print_date(dol_now(), '%Y%m%d'); header('Content-Type: text/csv'); -if ($conf->global->ACCOUNTING_EXPORT_MODELCSV == "11") // Specific filename for FEC model export +if ($conf->global->ACCOUNTING_EXPORT_MODELCSV == "11" && $type_export == "general_ledger") // Specific filename for FEC model export into the general ledger { - // FEC format is defined here: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000027804775&cidTexte=LEGITEXT000006069583&dateTexte=20130802&oldAction=rechCodeArticle if (empty($search_date_end)) { diff --git a/htdocs/adherents/admin/adherent_emails.php b/htdocs/adherents/admin/adherent_emails.php index feeb48eb1c3..9ee06cd3fa7 100644 --- a/htdocs/adherents/admin/adherent_emails.php +++ b/htdocs/adherents/admin/adherent_emails.php @@ -147,8 +147,9 @@ $constantes=array( 'ADHERENT_EMAIL_TEMPLATE_AUTOREGISTER' =>'emailtemplate:member', /* old was ADHERENT_AUTOREGISTER_MAIL */ 'ADHERENT_EMAIL_TEMPLATE_MEMBER_VALIDATION' =>'emailtemplate:member', /* old was ADHERENT_MAIL_VALID */ 'ADHERENT_EMAIL_TEMPLATE_SUBSCRIPTION' =>'emailtemplate:member', /* old was ADHERENT_MAIL_COTIS */ - 'ADHERENT_EMAIL_TEMPLATE_REMIND_EXPIRATION' =>'emailtemplate:member', 'ADHERENT_EMAIL_TEMPLATE_CANCELATION' =>'emailtemplate:member', /* old was ADHERENT_MAIL_RESIL */ + 'MEMBER_REMINDER_EMAIL'=>array('type'=>'yesno', 'label'=>$langs->trans('MEMBER_REMINDER_EMAIL', $langs->transnoentities("Module2300Name"))), + 'ADHERENT_EMAIL_TEMPLATE_REMIND_EXPIRATION' =>'emailtemplate:member', ); $helptext='*'.$langs->trans("FollowingConstantsWillBeSubstituted").'
'; diff --git a/htdocs/adherents/card.php b/htdocs/adherents/card.php index 51f7db4cd67..347c29fc30d 100644 --- a/htdocs/adherents/card.php +++ b/htdocs/adherents/card.php @@ -907,7 +907,7 @@ else // Login if (empty($conf->global->ADHERENT_LOGIN_NOT_REQUIRED)) { - print ''.$langs->trans("Login").' / '.$langs->trans("Id").'login).'">'; + print ''.$langs->trans("Login").' / '.$langs->trans("Id").'login).'" autofocus="autofocus">'; } // Password diff --git a/htdocs/adherents/class/adherent.class.php b/htdocs/adherents/class/adherent.class.php index e02517fc3d5..9d6c5d02bb4 100644 --- a/htdocs/adherents/class/adherent.class.php +++ b/htdocs/adherents/class/adherent.class.php @@ -2644,13 +2644,19 @@ class Adherent extends CommonObject $blockingerrormsg = ''; - /*if (empty($conf->global->MEMBER_REMINDER_EMAIL)) + if (empty($conf->adherent->enabled)) // Should not happen. If module disabled, cron job should not be visible. + { + $langs->load("agenda"); + $this->output = $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv("Adherent")); + return 0; + } + if (empty($conf->global->MEMBER_REMINDER_EMAIL)) { $langs->load("agenda"); $this->output = $langs->trans('EventRemindersByEmailNotEnabled', $langs->transnoentitiesnoconv("Adherent")); return 0; - }*/ - + } + $now = dol_now(); $nbok = 0; $nbko = 0; @@ -2736,7 +2742,58 @@ class Adherent extends CommonObject { $nbok++; - // TODO Add event email sent for member + $message = $msg; + $sendto = $to; + $sendtocc = ''; + $sendtobcc = ''; + $actioncode='EMAIL'; + $extraparams=''; + + $actionmsg=''; + $actionmsg2=$langs->transnoentities('MailSentBy').' '.CMailFile::getValidAddress($from,4,0,1).' '.$langs->transnoentities('To').' '.CMailFile::getValidAddress($sendto,4,0,1); + if ($message) + { + $actionmsg=$langs->transnoentities('MailFrom').': '.dol_escape_htmltag($from); + $actionmsg=dol_concatdesc($actionmsg, $langs->transnoentities('MailTo').': '.dol_escape_htmltag($sendto)); + if ($sendtocc) $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('Bcc') . ": " . dol_escape_htmltag($sendtocc)); + $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('MailTopic') . ": " . $subject); + $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('TextUsedInTheMessageBody') . ":"); + $actionmsg = dol_concatdesc($actionmsg, $message); + } + + require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; + + // Insert record of emails sent + $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 = $actionmsg2; + $actioncomm->note = $actionmsg; + $actioncomm->fk_project = 0; + $actioncomm->datep = $now; + $actioncomm->datef = $now; + $actioncomm->percentage = -1; // Not applicable + $actioncomm->socid = $adherent->thirdparty->id; + $actioncomm->contactid = 0; + $actioncomm->authorid = $user->id; // User saving action + $actioncomm->userownerid = $user->id; // Owner of action + // Fields when action is en email (content should be added into note) + $actioncomm->email_msgid = $cmail->msgid; + $actioncomm->email_from = $from; + $actioncomm->email_sender= ''; + $actioncomm->email_to = $to; + $actioncomm->email_tocc = $sendtocc; + $actioncomm->email_tobcc = $sendtobcc; + $actioncomm->email_subject = $subject; + $actioncomm->errors_to = ''; + + $actioncomm->fk_element = $adherent->id; + $actioncomm->elementtype = $adherent->element; + + $actioncomm->extraparams = $extraparams; + + $actioncomm->create($user); } } else diff --git a/htdocs/adherents/index.php b/htdocs/adherents/index.php index c7a1e665b11..beb8d44cdc3 100644 --- a/htdocs/adherents/index.php +++ b/htdocs/adherents/index.php @@ -55,7 +55,7 @@ $AdherentsResilies=array(); $AdherentType=array(); -// Liste les adherents +// Members list $sql = "SELECT t.rowid, t.libelle as label, t.subscription,"; $sql.= " d.statut, count(d.rowid) as somme"; $sql.= " FROM ".MAIN_DB_PREFIX."adherent_type as t"; @@ -92,7 +92,7 @@ if ($result) $now=dol_now(); -// List members up to date +// Members up to date list // current rule: uptodate = the end date is in future whatever is type // old rule: uptodate = if type does not need payment, that end date is null, if type need payment that end date is in future) $sql = "SELECT count(*) as somme , d.fk_adherent_type"; @@ -140,7 +140,7 @@ if (! empty($conf->global->MAIN_SEARCH_FORM_ON_HOME_AREAS)) // This is usele foreach($listofsearchfields as $key => $value) { if ($i == 0) print ''.$langs->trans("Search").''; - print ''; + print ''; print ':'; if ($i == 0) print ''; print ''; diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index 51e1a8b244e..39d0069ac7c 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -86,7 +86,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('adherent'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/adherents/subscription/list.php b/htdocs/adherents/subscription/list.php index 474082b1cfa..4ef43e8f316 100644 --- a/htdocs/adherents/subscription/list.php +++ b/htdocs/adherents/subscription/list.php @@ -53,13 +53,15 @@ $pagenext = $page + 1; if (! $sortorder) { $sortorder="DESC"; } if (! $sortfield) { $sortfield="c.dateadh"; } +$object = new Subscription($db); + // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('subscriptionlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('subscription'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/adherents/type.php b/htdocs/adherents/type.php index cb123294764..fc147ad8fa1 100644 --- a/htdocs/adherents/type.php +++ b/htdocs/adherents/type.php @@ -317,7 +317,7 @@ if ($action == 'create') print ''; print ''; - print ''; + print ''; print ''; print ''; - print ''; - print ''; - print ''; @@ -418,7 +421,7 @@ if ($rowid > 0) */ if ($action == 'delete') { - print $form->formconfirm($_SERVER['PHP_SELF']."?rowid=".$object->id,$langs->trans("DeleteAMemberType"),$langs->trans("ConfirmDeleteMemberType",$object->label),"confirm_delete", '',0,1); + print $form->formconfirm($_SERVER['PHP_SELF']."?rowid=".$object->id,$langs->trans("DeleteAnAssetType"),$langs->trans("ConfirmDeleteAssetType",$object->label),"confirm_delete", '',0,1); } $head = asset_type_prepare_head($object); @@ -427,14 +430,23 @@ if ($rowid > 0) $linkback = ''.$langs->trans("BackToList").''; - dol_banner_tab($object, 'rowid', $linkback); + $morehtmlref='
'; + // Ref asset type + $morehtmlref.=$form->editfieldkey("Label", 'label', $object->label, $object, $user->rights->asset->write, 'string', '', 0, 1); + $morehtmlref.=$form->editfieldval("Label", 'label', $object->label, $object, $user->rights->asset->write, 'string', '', null, null, '', 1); + $morehtmlref.='
'; + + dol_banner_tab($object, 'rowid', $linkback, 1, 'rowid', 'ref', $morehtmlref, '', 0, '', $morehtmlright); print '
'; print '
'; print '
'.$langs->trans("Label").'
'.$langs->trans("Label").'
'.$langs->trans("SubscriptionRequired").''; print $form->selectyesno("subscription",1,1); diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index c572b8abe8b..1b03101a2ae 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -618,7 +618,7 @@ if (GETPOST('actionadd') || GETPOST('actionmodify')) if ($value == 'formula' && empty($_POST['formula'])) continue; if ($value == 'sortorder') continue; // For a column name 'sortorder', we use the field name 'position' if ((! isset($_POST[$value]) || $_POST[$value]=='') - && (! in_array($listfield[$f], array('decalage','module','accountancy_code','accountancy_code_sell','accountancy_code_buy')) // Fields that are not mandatory + && (! in_array($listfield[$f], array('decalage','module','accountancy_code','accountancy_code_sell','accountancy_code_buy','tracking')) // Fields that are not mandatory && (! ($id == 10 && $listfield[$f] == 'code')) // Code is mandatory fir table 10 ) ) diff --git a/htdocs/admin/emailcollector_card.php b/htdocs/admin/emailcollector_card.php new file mode 100644 index 00000000000..3280492701e --- /dev/null +++ b/htdocs/admin/emailcollector_card.php @@ -0,0 +1,559 @@ + + * + * 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 . + */ + +/** + * \file htdocs/admin/emailcollector_card.php + * \ingroup emailcollector + * \brief Page to create/edit/view emailcollector + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/events.class.php'; + +include_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; +include_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; +include_once DOL_DOCUMENT_ROOT.'/emailcollector/class/emailcollector.class.php'; +include_once DOL_DOCUMENT_ROOT.'/emailcollector/class/emailcollectorfilter.class.php'; +include_once DOL_DOCUMENT_ROOT.'/emailcollector/class/emailcollectoraction.class.php'; +include_once DOL_DOCUMENT_ROOT.'/emailcollector/lib/emailcollector.lib.php'; + +if (!$user->admin) + accessforbidden(); + +// Load traductions files requiredby by page +$langs->loadLangs(array("admin", "mails", "other")); + +// Get parameters +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action', 'aZ09'); +$confirm = GETPOST('confirm', 'alpha'); +$cancel = GETPOST('cancel', 'aZ09'); +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'myobjectcard'; // To manage different context of search +$backtopage = GETPOST('backtopage', 'alpha'); + +// Initialize technical objects +$object = new EmailCollector($db); +$extrafields = new ExtraFields($db); +$diroutputmassaction = $conf->emailcollector->dir_output . '/temp/massgeneration/' . $user->id; +$hookmanager->initHooks(array('emailcollectorcard')); // Note that conf->hooks_modules contains array +// Fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label('emailcollector'); +$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); + +// Initialize array of search criterias +$search_all = trim(GETPOST("search_all", 'alpha')); +$search = array(); +foreach ($object->fields as $key => $val) { + if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha'); +} + +if (empty($action) && empty($id) && empty($ref)) $action='view'; + +// Load object +include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals + +// Security check - Protection if external user +//if ($user->societe_id > 0) access_forbidden(); +//if ($user->societe_id > 0) $socid = $user->societe_id; +//$isdraft = (($object->statut == MyObject::STATUS_DRAFT) ? 1 : 0); +//$result = restrictedArea($user, 'mymodule', $object->id, '', '', 'fk_soc', 'rowid', $isdraft); + + +/* + * Actions + */ + +$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)) +{ + $error = 0; + + $permissiontoadd=1; + $permissiontodelete=1; + if (empty($backtopage)) $backtopage = DOL_URL_ROOT.'/admin/emailcollector_card.php?id='.($id > 0 ? $id : '__ID__'); + $backurlforlist = DOL_URL_ROOT.'/admin/emailcollector_list.php'; + + // Actions cancel, add, update, delete or clone + include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php'; + + // Actions when linking object each other + include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be include, not include_once + + // Actions when printing a doc from card + include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php'; +} + + +if ($action == 'confirm_collect') +{ + dol_include_once('/emailcollector/class/emailcollector.class.php'); + + $res = $object->doCollect(); + + if ($res == 0) + { + setEventMessages($object->output, null, 'mesgs'); + } + else + { + setEventMessages($object->error, null, 'errors'); + } + + $action = ''; +} + +if (GETPOST('addfilter','alpha')) +{ + $emailcollectorfilter = new EmailCollectorFilter($db); + $emailcollectorfilter->type = GETPOST('filtertype','az09'); + $emailcollectorfilter->rulevalue = GETPOST('rulevalue', 'alpha'); + $emailcollectorfilter->fk_emailcollector = $object->id; + $emailcollectorfilter->status = 1; + $result = $emailcollectorfilter->create($user); + + if ($result > 0) + { + $object->fetchFilters(); + } + else + { + setEventMessages($emailcollectorfilter->errors, $emailcollectorfilter->error, 'errors'); + } +} + +if ($action == 'deletefilter') +{ + $emailcollectorfilter = new EmailCollectorFilter($db); + $emailcollectorfilter->fetch(GETPOST('filterid','int')); + $result = $emailcollectorfilter->delete($user); + if ($result > 0) + { + $object->fetchFilters(); + } + else + { + setEventMessages($emailcollectorfilter->errors, $emailcollectorfilter->error, 'errors'); + } +} + +if (GETPOST('addoperation','alpha')) +{ + $emailcollectoroperation = new EmailCollectorAction($db); + $emailcollectoroperation->type = GETPOST('operationtype','az09'); + $emailcollectoroperation->actionparam = GETPOST('actionparam', 'alpha'); + $emailcollectoroperation->fk_emailcollector = $object->id; + $emailcollectoroperation->status = 1; + $result = $emailcollectoroperation->create($user); + + if ($result > 0) + { + $object->fetchActions(); + } + else + { + setEventMessages($emailcollectoroperation->errors, $emailcollectoroperation->error, 'errors'); + } +} + +if ($action == 'deleteoperation') +{ + $emailcollectoroperation = new EmailCollectorAction($db); + $emailcollectoroperation->fetch(GETPOST('operationid','int')); + $result = $emailcollectoroperation->delete($user); + if ($result > 0) + { + $object->fetchActions(); + } + else + { + setEventMessages($emailcollectoroperation->errors, $emailcollectoroperation->error, 'errors'); + } +} + + + +/* + * View + */ + +$form = new Form($db); +$formfile = new FormFile($db); + +llxHeader('', 'EmailCollector', ''); + +// Example : Adding jquery code +print ''; + +// Part to create +if ($action == 'create') { + print load_fiche_titre($langs->trans("NewEmailCollector", $langs->transnoentitiesnoconv("EmailCollector"))); + + print '
'; + print ''; + print ''; + print ''; + + dol_fiche_head(array(), ''); + + print ''."\n"; + + //unset($fields[]); + + // Common attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_add.tpl.php'; + + // Other attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_add.tpl.php'; + + print '
'."\n"; + + dol_fiche_end(); + + print '
'; + print ''; + print '  '; + print ''; // Cancel for create does not post form if we don't know the backtopage + print '
'; + + print '
'; +} + +// Part to edit record +if (($id || $ref) && $action == 'edit') +{ + print load_fiche_titre($langs->trans("EmailCollector")); + + print '
'; + print ''; + print ''; + print ''; + print ''; + + dol_fiche_head(); + + print '' . "\n"; + + // Common attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_edit.tpl.php'; + + // Other attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_edit.tpl.php'; + + print '
'; + + dol_fiche_end(); + + print '
'; + print '   '; + print '
'; + + print '
'; +} + +// Part to show record +if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) +{ + $res = $object->fetch_optionals(); + + $object->fetchFilters(); + $object->fetchActions(); + + $head = emailcollectorPrepareHead($object); + dol_fiche_head($head, 'card', $langs->trans("EmailCollector"), -1, 'emailcollector'); + + $formconfirm = ''; + + // Confirmation to delete + if ($action == 'delete') + { + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('DeleteEmailCollector'), $langs->trans('ConfirmDeleteEmailCollector'), 'confirm_delete', '', 0, 1); + } + + // Clone confirmation + if ($action == 'clone') { + // Create an array for form + $formquestion = array(); + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('CloneMyObject'), $langs->trans('ConfirmCloneMyObject', $object->ref), 'confirm_clone', $formquestion, 'yes', 1); + } + + // Confirmation of action process + if ($action == 'collect') { + $formquestion = array( + 'text' => $langs->trans("EmailCollectorConfirmCollect"), + ); + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('EmailCollectorConfirmCollectTitle'), $text, 'confirm_collect', $formquestion, 0, 1, 220); + } + + // Call Hook formConfirm + $parameters = array('lineid' => $lineid); + $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; + + // Object card + // ------------------------------------------------------------ + $linkback = '' . $langs->trans("BackToList") . ''; + + $morehtmlref = '
'; + /* + // Ref bis + $morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->emailcollector->creer, 'string', '', 0, 1); + $morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->emailcollector->creer, 'string', '', null, null, '', 1); + // Thirdparty + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $soc->getNomUrl(1); + // Project + if (! empty($conf->projet->enabled)) + { + $langs->load("projects"); + $morehtmlref.='
'.$langs->trans('Project') . ' '; + if ($user->rights->emailcollector->creer) + { + if ($action != 'classify') + { + $morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('SetProject')) . ' : '; + if ($action == 'classify') { + //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); + $morehtmlref.='
'; + $morehtmlref.=''; + $morehtmlref.=''; + $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1); + $morehtmlref.=''; + $morehtmlref.='
'; + } else { + $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); + } + } + } else { + if (! empty($object->fk_project)) { + $proj = new Project($db); + $proj->fetch($object->fk_project); + $morehtmlref.=''; + $morehtmlref.=$proj->ref; + $morehtmlref.=''; + } else { + $morehtmlref.=''; + } + } + } + */ + $morehtmlref .= '
'; + + dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref); + + print '
'; + print '
'; + print '
'; + print ''."\n"; + + // Common attributes + //$keyforbreak='fieldkeytoswithonsecondcolumn'; + include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_view.tpl.php'; + + // Other attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php'; + + print '
'; + + + print '
'; + print ''; + print ''; + print ''; + print ''; + + // Filters + print ''; + print ''; + print ''; + print ''; + // Add filter + print ''; + print ''; + print ''; + print ''; + // List filters + foreach($object->filters as $rulefilter) + { + $rulefilterobj=new EmailCollectorFilter($db); + $rulefilterobj->fetch($rulefilter['id']); + + print ''; + print ''; + print ''; + print ''; + print ''; + } + + print ''; + print '
'.$langs->trans("Filters").'
'; + $arrayoftypes=array('from'=>'MailFrom', 'to'=>'MailTo', 'cc'=>'Cc', 'bcc'=>'Bcc', 'subject'=>'Subject', 'body'=>'Body', 'seen'=>'AlreadyRead', 'unseen'=>'NotRead'); + print $form->selectarray('filtertype', $arrayoftypes, '', 1, 0, 0, '', 1); + print ''; + print ''; + print '
'; + print $langs->trans($arrayoftypes[$rulefilter['type']]); + print ''.$rulefilter['rulevalue'].''; + //print $rulefilterobj->getLibStatut(3); + print ' '.img_delete().''; + print '
'; + + print '

'; + + // Operations + print ''; + print ''; + print ''; + print ''; + // Add operation + print ''; + print ''; + print ''; + print ''; + // List operations + foreach($object->actions as $ruleaction) + { + $ruleactionobj=new EmailcollectorAction($db); + $ruleactionobj->fetch($ruleaction['id']); + + print ''; + print ''; + print ''; + print ''; + print ''; + } + + print ''; + print '
'.$langs->trans("EmailcollectorOperations").'
'; + $arrayoftypes=array('recordevent'=>'RecordEvent'); + if ($conf->projet->enabled) $arrayoftypes['project']='CreateLeadAndThirdParty'; + print $form->selectarray('operationtype', $arrayoftypes, '', 0, 0, 0, '', 1); + print ''; + print ''; + print '
'; + print $langs->trans($arrayoftypes[$ruleaction['type']]); + print ''.$ruleaction['actionparam'].''; + //print $ruleactionobj->getLibStatut(3); + print ' '.img_delete().''; + print '
'; + + print '
'; + + print '
'; + print '
'; + + + print '

'; + + dol_fiche_end(); + + // Buttons for actions + if ($action != 'presend' && $action != 'editline') { + print '
' . "\n"; + $parameters = array(); + $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $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)) + { + print '' . $langs->trans("Edit") . '' . "\n"; + + print '' . $langs->trans("CollectNow") . '' . "\n"; + + print '' . $langs->trans('Delete') . '' . "\n"; + } + print '
' . "\n"; + } + + // Select mail models is same action as presend + if (GETPOST('modelselected')) { + $action = 'presend'; + } + + /* + if ($action != 'presend') { + print '
'; + print ''; // ancre + */ + // Documents + /*$objref = dol_sanitizeFileName($object->ref); + $relativepath = $comref . '/' . $comref . '.pdf'; + $filedir = $conf->emailcollector->dir_output . '/' . $objref; + $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id; + $genallowed = $user->rights->emailcollector->read; // If you can read, you can build the PDF to read content + $delallowed = $user->rights->emailcollector->create; // If you can create/edit, you can remove a file on card + print $formfile->showdocuments('emailcollector', $objref, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang); + */ + /* + // Show links to link elements + $linktoelem = $form->showLinkToObjectBlock($object, null, array('emailcollector')); + $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem); + + print '
'; + + $MAXEVENT = 10; + + $morehtmlright = ''; + $morehtmlright .= $langs->trans("SeeAll"); + $morehtmlright .= ''; + + // List of actions on element + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php'; + $formactions = new FormActions($db); + $somethingshown = $formactions->showactions($object, 'emailcollector_emailcollector', $socid, 1, '', $MAXEVENT, '', $morehtmlright); + + print '
'; + } + */ + + //Select mail models is same action as presend + /* + if (GETPOST('modelselected')) $action = 'presend'; + + // Presend form + $modelmail='inventory'; + $defaulttopic='InformationMessage'; + $diroutput = $conf->product->dir_output.'/inventory'; + $trackid = 'stockinv'.$object->id; + + include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php'; + */ +} + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/admin/emailcollector_list.php b/htdocs/admin/emailcollector_list.php new file mode 100644 index 00000000000..71169ddffb0 --- /dev/null +++ b/htdocs/admin/emailcollector_list.php @@ -0,0 +1,551 @@ + + * + * 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 . + */ + +/** + * \file htdocs/admin/emailcollector_list.php + * \ingroup emailcollector + * \brief List page for emailcollector + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/events.class.php'; + +require_once DOL_DOCUMENT_ROOT . '/core/class/html.formcompany.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php'; +dol_include_once('/emailcollector/class/emailcollector.class.php'); + +if (!$user->admin) accessforbidden(); + +// Load traductions files requiredby by page +$langs->loadLangs(array("admin", "other")); + +$action = GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view'; // The action 'add', 'create', 'edit', 'update', 'view', ... +$massaction = GETPOST('massaction','alpha'); // The bulk action (combo box choice into lists) +$show_files = GETPOST('show_files','int'); // Show files area generated by bulk actions ? +$confirm = GETPOST('confirm','alpha'); // Result of a confirmation +$cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button +$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'emailcollectorlist'; // To manage different context of search +$backtopage = GETPOST('backtopage','alpha'); // Go back to a dedicated page +$optioncss = GETPOST('optioncss','aZ'); // Option for the css output (always '' except when 'print') + +$id = GETPOST('id','int'); + +// Load variable for pagination +$limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit; +$sortfield = GETPOST('sortfield','alpha'); +$sortorder = GETPOST('sortorder','alpha'); +$page = GETPOST('page','int'); +if (empty($page) || $page == -1 || GETPOST('button_search','alpha') || GETPOST('button_removefilter','alpha') || (empty($toselect) && $massaction === '0')) { $page = 0; } // If $page is not defined, or '' or -1 or if we click on clear filters or if we select empty mass action +$offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; +//if (! $sortfield) $sortfield="p.date_fin"; +//if (! $sortorder) $sortorder="DESC"; + +// Initialize technical objects +$object = new EmailCollector($db); +$extrafields = new ExtraFields($db); +$diroutputmassaction = $conf->emailcollector->dir_output . '/temp/massgeneration/' . $user->id; +$hookmanager->initHooks(array('emailcollectorlist')); // Note that conf->hooks_modules contains array +// Fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label('emailcollector'); +$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); + +// Default sort order (if not yet defined by previous GETPOST) +if (! $sortfield) $sortfield="t.".key($object->fields); // Set here default search field. By default 1st field in definition. +if (! $sortorder) $sortorder="ASC"; + +// Security check +$socid=0; +if ($user->societe_id > 0) // Protection if external user +{ + //$socid = $user->societe_id; + accessforbidden(); +} +//$result = restrictedArea($user, 'emailcollector', $id, ''); + +// Initialize array of search criterias +$search_all=trim(GETPOST("search_all",'alpha')); +$search=array(); +foreach($object->fields as $key => $val) +{ + if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha'); +} + +// List of fields to search into when doing a "search in all" +$fieldstosearchall = array(); +foreach($object->fields as $key => $val) +{ + if ($val['searchall']) $fieldstosearchall['t.'.$key]=$val['label']; +} + +// Definition of fields for list +$arrayfields=array(); +foreach($object->fields as $key => $val) +{ + // If $val['visible']==0, then we never show the field + if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>$val['enabled'], 'position'=>$val['position']); +} +// Extra fields +if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) +{ + foreach($extrafields->attributes[$object->table_element]['label'] as $key => $val) + { + if (! empty($extrafields->attributes[$object->table_element]['list'][$key])) + $arrayfields["ef.".$key]=array('label'=>$extrafields->attributes[$object->table_element]['label'][$key], 'checked'=>(($extrafields->attributes[$object->table_element]['list'][$key]<0)?0:1), 'position'=>$extrafields->attributes[$object->table_element]['pos'][$key], 'enabled'=>(abs($extrafields->attributes[$object->table_element]['list'][$key])!=3 && $extrafields->attributes[$object->table_element]['perms'][$key])); + } +} +$object->fields = dol_sort_array($object->fields, 'position'); +$arrayfields = dol_sort_array($arrayfields, 'position'); + + + +/* + * Actions + * + * Put here all code to do according to value of "$action" parameter + */ + +if (GETPOST('cancel','alpha')) { $action='list'; $massaction=''; } +if (! GETPOST('confirmmassaction','alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction=''; } + +$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)) +{ + // Selection of new fields + include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; + + // Purge search criteria + if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers + { + foreach($object->fields as $key => $val) + { + $search[$key]=''; + } + $toselect=''; + $search_array_options=array(); + } + if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha') + || GETPOST('button_search_x','alpha') || GETPOST('button_search.x','alpha') || GETPOST('button_search','alpha')) + { + $massaction=''; // Protection to avoid mass action if we force a new search during a mass action confirmation + } + + // Mass actions + $objectclass='EmailCollector'; + $objectlabel='EmailCollector'; + $permtoread = $user->rights->emailcollector->read; + $permtodelete = $user->rights->emailcollector->delete; + $uploaddir = $conf->emailcollector->dir_output; + include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php'; +} + + + +/* + * View + * + * Put here all code to render page + */ + +$form=new Form($db); + +$now=dol_now(); + +//$help_url="EN:Module_EmailCollector|FR:Module_EmailCollector_FR|ES:Módulo_EmailCollector"; +$help_url=''; +$title = $langs->trans('ListOf', $langs->transnoentitiesnoconv("EmailCollector")); + + +// Build and execute select +// -------------------------------------------------------------------- +$sql = 'SELECT '; +foreach($object->fields as $key => $val) +{ + $sql.='t.'.$key.', '; +} +// Add fields from extrafields +if (! empty($extrafields->attributes[$object->table_element]['label'])) + foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.' as options_'.$key.', ' : ''); +// Add fields from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; +$sql=preg_replace('/, $/','', $sql); +$sql.= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t"; +if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)"; +if ($object->ismultientitymanaged == 1) $sql.= " WHERE t.entity IN (".getEntity($object->element).")"; +else $sql.=" WHERE 1 = 1"; +foreach($search as $key => $val) +{ + if ($key == 'status' && $search[$key] == -1) continue; + $mode_search=(($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key]))?1:0); + if ($search[$key] != '') $sql.=natural_search($key, $search[$key], (($key == 'status')?2:$mode_search)); +} +if ($search_all) $sql.= natural_search(array_keys($fieldstosearchall), $search_all); +// Add where from extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; +// Add where from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; + +/* If a group by is required + $sql.= " GROUP BY " + foreach($object->fields as $key => $val) + { + $sql.='t.'.$key.', '; + } + // Add fields from extrafields + if (! empty($extrafields->attributes[$object->table_element]['label'])) { + foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.', ' : ''); + // Add where from hooks + $parameters=array(); + $reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters); // Note that $action and $object may have been modified by hook + $sql.=$hookmanager->resPrint; + $sql=preg_replace('/, $/','', $sql); + */ + +$sql.=$db->order($sortfield,$sortorder); + +// Count total nb of records +$nbtotalofrecords = ''; +if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) +{ + $resql = $db->query($sql); + $nbtotalofrecords = $db->num_rows($resql); + if (($page * $limit) > $nbtotalofrecords) // if total of record found is smaller than page * limit, goto and load page 0 + { + $page = 0; + $offset = 0; + } +} +// if total of record found is smaller than limit, no need to do paging and to restart another select with limits set. +if (is_numeric($nbtotalofrecords) && $limit > $nbtotalofrecords) +{ + $num = $nbtotalofrecords; +} +else +{ + $sql.= $db->plimit($limit+1, $offset); + + $resql=$db->query($sql); + if (! $resql) + { + dol_print_error($db); + exit; + } + + $num = $db->num_rows($resql); +} + +// Direct jump if only one record found +if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all) +{ + $obj = $db->fetch_object($resql); + $id = $obj->rowid; + header("Location: ".DOL_URL_ROOT.'/emailcollector/emailcollector_card.php?id='.$id); + exit; +} + + +// Output page +// -------------------------------------------------------------------- + +llxHeader('', $title, $help_url); + +// Example : Adding jquery code +print ''; + +$arrayofselected=is_array($toselect)?$toselect:array(); + +$param=''; +if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); +if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); +foreach($search as $key => $val) +{ + $param.= '&search_'.$key.'='.urlencode($search[$key]); +} +if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); +// Add $param from extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; + +// List of mass actions available +$arrayofmassactions = array( +//'presend'=>$langs->trans("SendByMail"), +//'builddoc'=>$langs->trans("PDFMerge"), +); +if ($user->rights->emailcollector->delete) $arrayofmassactions['predelete']=$langs->trans("Delete"); +if (GETPOST('nomassaction','int') || in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); +$massactionbutton=$form->selectMassAction('', $arrayofmassactions); + +print '
'; +if ($optioncss != '') print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +$newcardbutton=''; +//if ($user->rights->emailcollector->creer) + //{ +$newcardbutton=''.$langs->trans('New').''; +$newcardbutton.= ''; +$newcardbutton.= ''; +//} + +print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies', 0, $newcardbutton, '', $limit); + +// Add code for pre mass action (confirmation or email presend form) +include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; + +if ($sall) +{ + foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val); + print '
'.$langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall).'
'; +} + +$moreforfilter = ''; +/*$moreforfilter.='
'; + $moreforfilter.= $langs->trans('MyFilter') . ': '; + $moreforfilter.= '
';*/ + +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint; +else $moreforfilter = $hookmanager->resPrint; + +if (! empty($moreforfilter)) +{ + print '
'; + print $moreforfilter; + print '
'; +} + +$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; +$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields +$selectedfields.=(count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : ''); + +print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table +print ''."\n"; + + +// Fields title search +// -------------------------------------------------------------------- +print ''; +foreach($object->fields as $key => $val) +{ + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) print ''; +} +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; + +// Fields from hook +$parameters=array('arrayfields'=>$arrayfields); +$reshook=$hookmanager->executeHooks('printFieldListOption', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +// Action column +print ''; +print ''."\n"; + + +// Fields title label +// -------------------------------------------------------------------- +print ''; +foreach($object->fields as $key => $val) +{ + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($align?'class="'.$align.'"':''), $sortfield, $sortorder, $align.' ')."\n"; +} +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; +// Hook fields +$parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); +$reshook=$hookmanager->executeHooks('printFieldListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],'','','','align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n"; +print ''."\n"; + + +// Detect if we need a fetch on each output line +$needToFetchEachLine=0; +if (is_array($extrafields->attributes[$object->table_element]['computed']) && count($extrafields->attributes[$object->table_element]['computed']) > 0) +{ + foreach ($extrafields->attributes[$object->table_element]['computed'] as $key => $val) + { + if (preg_match('/\$object/',$val)) $needToFetchEachLine++; // There is at least one compute field that use $object + } +} + + +// Loop on record +// -------------------------------------------------------------------- +$i=0; +$totalarray=array(); +while ($i < min($num, $limit)) +{ + $obj = $db->fetch_object($resql); + if (empty($obj)) break; // Should not happen + + // Store properties in $object + $object->id = $obj->rowid; + foreach($object->fields as $key => $val) + { + if (isset($obj->$key)) $object->$key = $obj->$key; + } + + // Show here line of result + print ''; + foreach($object->fields as $key => $val) + { + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) + { + print ''; + print $object->showOutputField($val, $key, $obj->$key, ''); + print ''; + if (! $i) $totalarray['nbfield']++; + if (! empty($val['isameasure'])) + { + if (! $i) $totalarray['pos'][$totalarray['nbfield']]='t.'.$key; + $totalarray['val']['t.'.$key] += $obj->$key; + } + } + } + // Extra fields + include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; + // Fields from hook + $parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj); + $reshook=$hookmanager->executeHooks('printFieldListValue', $parameters, $object); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // Action column + print ''; + if (! $i) $totalarray['nbfield']++; + + print ''; + + $i++; +} + +// Show total line +if (isset($totalarray['pos'])) +{ + print ''; + $i=0; + while ($i < $totalarray['nbfield']) + { + $i++; + if (! empty($totalarray['pos'][$i])) print ''; + else + { + if ($i == 1) + { + if ($num < $limit) print ''; + else print ''; + } + else print ''; + } + } + print ''; +} + +// If no record found +if ($num == 0) +{ + $colspan=1; + foreach($arrayfields as $key => $val) { if (! empty($val['checked'])) $colspan++; } + print ''; +} + + +$db->free($resql); + +$parameters=array('arrayfields'=>$arrayfields, 'sql'=>$sql); +$reshook=$hookmanager->executeHooks('printFieldListFooter', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; + +print '
'; +$searchpicto=$form->showFilterButtons(); +print $searchpicto; +print '
'; + if ($massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + { + $selected=0; + if (in_array($obj->rowid, $arrayofselected)) $selected=1; + print ''; + } + print '
'.price($totalarray['val'][$totalarray['pos'][$i]]).''.$langs->trans("Total").''.$langs->trans("Totalforthispage").'
'.$langs->trans("NoRecordFound").'
'."\n"; +print '
'."\n"; + +print '
'."\n"; + +if (in_array('builddoc',$arrayofmassactions) && ($nbtotalofrecords === '' || $nbtotalofrecords)) +{ + $hidegeneratedfilelistifempty=1; + if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) $hidegeneratedfilelistifempty=0; + + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; + $formfile = new FormFile($db); + + // Show list of available documents + $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder; + $urlsource.=str_replace('&','&',$param); + + $filedir=$diroutputmassaction; + $genallowed=$user->rights->emailcollector->read; + $delallowed=$user->rights->emailcollector->create; + + print $formfile->showdocuments('massfilesarea_emailcollector','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'','','',null,$hidegeneratedfilelistifempty); +} + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/admin/holiday.php b/htdocs/admin/holiday.php index c1c5fe58b45..82037fa85d1 100644 --- a/htdocs/admin/holiday.php +++ b/htdocs/admin/holiday.php @@ -75,8 +75,8 @@ else if ($action == 'specimen') // For contract { $modele= GETPOST('module','alpha'); - $contract = new Contrat($db); - $contract->initAsSpecimen(); + $holiday = new Holiday($db); + $holiday->initAsSpecimen(); // Search template files $file=''; $classname=''; $filefound=0; @@ -98,7 +98,7 @@ else if ($action == 'specimen') // For contract $module = new $classname($db); - if ($module->write_file($contract,$langs) > 0) + if ($module->write_file($holiday,$langs) > 0) { header("Location: ".DOL_URL_ROOT."/document.php?modulepart=holiday&file=SPECIMEN.pdf"); return; @@ -271,7 +271,7 @@ foreach ($dirmodels as $reldir) // Info $htmltooltip=''; $htmltooltip.=''.$langs->trans("Version").': '.$module->getVersion().'
'; - $nextval=$module->getNextValue($mysoc,$contract); + $nextval=$module->getNextValue($mysoc,$holiday); if ("$nextval" != $langs->trans("NotAvailable")) { // Keep " on nextval $htmltooltip.=''.$langs->trans("NextValue").': '; if ($nextval) { diff --git a/htdocs/admin/mails_senderprofile_list.php b/htdocs/admin/mails_senderprofile_list.php index 4914152f680..280025b3b35 100644 --- a/htdocs/admin/mails_senderprofile_list.php +++ b/htdocs/admin/mails_senderprofile_list.php @@ -61,7 +61,7 @@ $diroutputmassaction=$conf->admin->dir_output . '/temp/massgeneration/'.$user->i $hookmanager->initHooks(array('emailsenderprofilelist')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('emailsenderprofile'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Default sort order (if not yet defined by previous GETPOST) if (! $sortfield) $sortfield="t.".key($object->fields); // Set here default search field. By default 1st field in definition. diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php index f4c3e5b0923..2376cc58988 100644 --- a/htdocs/admin/modules.php +++ b/htdocs/admin/modules.php @@ -41,6 +41,7 @@ $langs->loadLangs(array("errors","admin","modulebuilder")); $mode=GETPOST('mode', 'alpha'); if (empty($mode)) $mode='common'; $action=GETPOST('action','alpha'); +//var_dump($_POST);exit; $value=GETPOST('value', 'alpha'); $page_y=GETPOST('page_y','int'); $search_keyword=GETPOST('search_keyword','alpha'); @@ -1010,8 +1011,39 @@ if ($mode == 'deploy') print '
'; print ''; print ''; - print $langs->trans("YouCanSubmitFile").' '; + + print $langs->trans("YouCanSubmitFile"); + + $max=$conf->global->MAIN_UPLOAD_DOC; // En Kb + $maxphp=@ini_get('upload_max_filesize'); // En inconnu + if (preg_match('/k$/i',$maxphp)) $maxphp=$maxphp*1; + if (preg_match('/m$/i',$maxphp)) $maxphp=$maxphp*1024; + if (preg_match('/g$/i',$maxphp)) $maxphp=$maxphp*1024*1024; + if (preg_match('/t$/i',$maxphp)) $maxphp=$maxphp*1024*1024*1024; + // Now $max and $maxphp are in Kb + $maxmin = $max; + if ($maxphp > 0) $maxmin=min($max,$maxphp); + + if ($maxmin > 0) + { + print ''."\n"; + // MAX_FILE_SIZE doit précéder le champ input de type file + print ''; + } + + print ' '; + print ''; + print '
'; print '
'; diff --git a/htdocs/api/class/api_documents.class.php b/htdocs/api/class/api_documents.class.php index 66c293f2cb0..8db471855d6 100644 --- a/htdocs/api/class/api_documents.class.php +++ b/htdocs/api/class/api_documents.class.php @@ -103,7 +103,7 @@ class Documents extends DolibarrApi } $file_content=file_get_contents($original_file_osencoded); - return array('filename'=>$filename, 'content'=>base64_encode($file_content), 'encoding'=>'MIME base64 (base64_encode php function, http://php.net/manual/en/function.base64-encode.php)' ); + return array('filename'=>$filename, 'content-type' => dol_mimetype($filename), 'filesize'=>filesize($original_file), 'content'=>base64_encode($file_content), 'encoding'=>'base64' ); } @@ -224,10 +224,9 @@ class Documents extends DolibarrApi } $file_content=file_get_contents($original_file_osencoded); - return array('filename'=>$filename, 'content'=>base64_encode($file_content), 'langcode'=>$outputlangs->defaultlang, 'template'=>$templateused, 'encoding'=>'MIME base64 (base64_encode php function, http://php.net/manual/en/function.base64-encode.php)' ); + return array('filename'=>$filename, 'content-type' => dol_mimetype($filename), 'filesize'=>filesize($original_file), 'content'=>base64_encode($file_content), 'langcode'=>$outputlangs->defaultlang, 'template'=>$templateused, 'encoding'=>'base64' ); } - /** * Return the list of documents of a dedicated element (from its ID or Ref) * diff --git a/htdocs/asset/card.php b/htdocs/asset/card.php index 4f7061aed20..40b2a9365db 100644 --- a/htdocs/asset/card.php +++ b/htdocs/asset/card.php @@ -45,7 +45,7 @@ $diroutputmassaction=$conf->asset->dir_output . '/temp/massgeneration/'.$user->i $hookmanager->initHooks(array('assetcard')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('asset'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Initialize array of search criterias $search_all=trim(GETPOST("search_all",'alpha')); diff --git a/htdocs/asset/class/asset_type.class.php b/htdocs/asset/class/asset_type.class.php index 8eb915f95eb..51d76d75155 100644 --- a/htdocs/asset/class/asset_type.class.php +++ b/htdocs/asset/class/asset_type.class.php @@ -42,7 +42,7 @@ class AssetType extends CommonObject /** * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png */ - public $picto = 'group'; + public $picto = 'invoice'; /** * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe @@ -51,9 +51,9 @@ class AssetType extends CommonObject public $ismultientitymanaged = 1; /** - * @var string Asset type label - */ - public $label; + * @var string Asset type label + */ + public $label; /** @var string Accountancy code asset */ public $accountancy_code_asset; @@ -107,12 +107,14 @@ class AssetType extends CommonObject $sql.= ", accountancy_code_asset"; $sql.= ", accountancy_code_depreciation_asset"; $sql.= ", accountancy_code_depreciation_expense"; + $sql.= ", note"; $sql.= ", entity"; $sql.= ") VALUES ("; $sql.= "'".$this->db->escape($this->label)."'"; $sql.= ", '".$this->db->escape($this->accountancy_code_asset)."'"; $sql.= ", '".$this->db->escape($this->accountancy_code_depreciation_asset)."'"; $sql.= ", '".$this->db->escape($this->accountancy_code_depreciation_expense)."'"; + $sql.= ", '".$this->db->escape($this->note)."'"; $sql.= ", ".$conf->entity; $sql.= ")"; @@ -179,7 +181,8 @@ class AssetType extends CommonObject $sql.= "label = '".$this->db->escape($this->label) ."',"; $sql.= "accountancy_code_asset = '".$this->db->escape($this->accountancy_code_asset)."',"; $sql.= "accountancy_code_depreciation_asset = '".$this->db->escape($this->accountancy_code_depreciation_asset)."',"; - $sql.= "accountancy_code_depreciation_expense = '".$this->db->escape($this->accountancy_code_depreciation_expense)."'"; + $sql.= "accountancy_code_depreciation_expense = '".$this->db->escape($this->accountancy_code_depreciation_expense)."',"; + $sql.= "note = '".$this->db->escape($this->note) ."'"; $sql.= " WHERE rowid =".$this->id; $result = $this->db->query($sql); diff --git a/htdocs/asset/list.php b/htdocs/asset/list.php index fa965aade5b..431276b67a8 100644 --- a/htdocs/asset/list.php +++ b/htdocs/asset/list.php @@ -62,7 +62,7 @@ $diroutputmassaction=$conf->asset->dir_output . '/temp/massgeneration/'.$user->i $hookmanager->initHooks(array('assetlist')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('asset'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Default sort order (if not yet defined by previous GETPOST) if (! $sortfield) $sortfield="t.".key($object->fields); // Set here default search field. By default 1st field in definition. diff --git a/htdocs/asset/type.php b/htdocs/asset/type.php index 9e06c4a765d..b8a282430cc 100644 --- a/htdocs/asset/type.php +++ b/htdocs/asset/type.php @@ -49,10 +49,13 @@ $offset = $limit * $page ; $pageprev = $page - 1; $pagenext = $page + 1; if (! $sortorder) { $sortorder="DESC"; } -if (! $sortfield) { $sortfield="d.lastname"; } +if (! $sortfield) { $sortfield="a.label"; } $label=GETPOST("label","alpha"); -$comment=GETPOST("comment"); +$accountancy_code_asset=GETPOST('accountancy_code_asset','string'); +$accountancy_code_depreciation_asset=GETPOST('accountancy_code_depreciation_asset','string'); +$accountancy_code_depreciation_expense=GETPOST('accountancy_code_depreciation_expense','string'); +$comment=GETPOST('comment','string'); // Security check $result=restrictedArea($user,'asset',$rowid,'asset_type'); @@ -259,39 +262,39 @@ if (! $rowid && $action != 'create' && $action != 'edit') print '
'.dol_escape_htmltag($objp->label).''; + print ''; if (! empty($conf->accounting->enabled)) { $accountingaccount = new AccountingAccount($db); - $accountingaccount->fetch('',$object->accountancy_code_asset,1); + $accountingaccount->fetch('',$objp->accountancy_code_asset,1); - print $accountingaccount->getNomUrl(0,1,1,'',1); + print $accountingaccount->getNomUrl(0,0,0,'',0); } else { - print $object->accountancy_code_asset; + print $objp->accountancy_code_asset; } print ''; + print ''; if (! empty($conf->accounting->enabled)) { $accountingaccount2 = new AccountingAccount($db); - $accountingaccount2->fetch('',$object->accountancy_code_depreciation_asset,1); + $accountingaccount2->fetch('',$objp->accountancy_code_depreciation_asset,1); - print $accountingaccount2->getNomUrl(0,1,1,'',1); + print $accountingaccount2->getNomUrl(0,0,0,'',0); } else { - print $object->accountancy_code_depreciation_asset; + print $objp->accountancy_code_depreciation_asset; } print ''; + print ''; if (! empty($conf->accounting->enabled)) { $accountingaccount3 = new AccountingAccount($db); - $accountingaccount3->fetch('',$object->accountancy_code_depreciation_expense,1); + $accountingaccount3->fetch('',$objp->accountancy_code_depreciation_expense,1); - print $accountingaccount3->getNomUrl(0,1,1,'',1); + print $accountingaccount3->getNomUrl(0,0,0,'',0); } else { - print $object->accountancy_code_depreciation_expense; + print $objp->accountancy_code_depreciation_expense; } print '
'; - print ''; + print ''; + print ''; + print ''; - print ''; + print ''; + print ''; - print ''; + print ''; print ''; print ''; - - print '
'; - print_barre_liste('',$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$nbtotalofrecords); - - $moreforfilter = ''; - - print '
'; - print '
'; + print $langs->trans("AccountancyCodeAsset"); + print ''; if (! empty($conf->accounting->enabled)) { $accountingaccount = new AccountingAccount($db); @@ -445,8 +457,12 @@ if ($rowid > 0) print $object->accountancy_code_asset; } print '
'; + print '
'; + print $langs->trans("AccountancyCodeDepreciationAsset"); + print ''; if (! empty($conf->accounting->enabled)) { $accountingaccount2 = new AccountingAccount($db); @@ -457,8 +473,12 @@ if ($rowid > 0) print $object->accountancy_code_depreciation_asset; } print '
'; + print '
'; + print $langs->trans("AccountancyCodeDepreciationExpense"); + print ''; if (! empty($conf->accounting->enabled)) { $accountingaccount3 = new AccountingAccount($db); @@ -468,6 +488,7 @@ if ($rowid > 0) } else { print $object->accountancy_code_depreciation_expense; } + print '
'.$langs->trans("Description").''; @@ -493,9 +514,6 @@ if ($rowid > 0) print ''; } - // Add - print ''; - // Delete if ($user->rights->asset->write) { @@ -503,234 +521,6 @@ if ($rowid > 0) } print ""; - - - // Show list of assets (nearly same code than in page list.php) - $assettypestatic=new AssetType($db); - - $now=dol_now(); - - $sql = "SELECT a.rowid, d.login, d.firstname, d.lastname, d.societe, "; - $sql.= " d.datefin,"; - $sql.= " a.fk_asset_type as type_id,"; - $sql.= " t.label as type"; - $sql.= " FROM ".MAIN_DB_PREFIX."asset as a, ".MAIN_DB_PREFIX."asset_type as t"; - $sql.= " WHERE a.fk_asset_type = t.rowid"; - $sql.= " AND a.entity IN (".getEntity('asset').")"; - $sql.= " AND t.rowid = ".$object->id; - if ($sall) - { - $sql.=natural_search(array("f.firstname","d.lastname","d.societe","d.email","d.login","d.address","d.town","d.note_public","d.note_private"), $sall); - } - if ($status != '') - { - $sql.= natural_search('d.statut', $status, 2); - } - if ($action == 'search') - { - if (GETPOST('search','alpha')) - { - $sql.= natural_search(array("d.firstname","d.lastname"), GETPOST('search','alpha')); - } - } - if (! empty($search_lastname)) - { - $sql.= natural_search(array("d.firstname","d.lastname"), $search_lastname); - } - if (! empty($search_login)) - { - $sql.= natural_search("d.login", $search_login); - } - if (! empty($search_email)) - { - $sql.= natural_search("d.email", $search_email); - } - if ($filter == 'uptodate') - { - $sql.=" AND datefin >= '".$db->idate($now)."'"; - } - if ($filter == 'outofdate') - { - $sql.=" AND datefin < '".$db->idate($now)."'"; - } - - $sql.= " ".$db->order($sortfield,$sortorder); - - // Count total nb of records - $nbtotalofrecords = ''; - if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) - { - $resql = $db->query($sql); - if ($resql) $nbtotalofrecords = $db->num_rows($result); - else dol_print_error($db); - if (($page * $limit) > $nbtotalofrecords) // if total resultset is smaller then paging size (filtering), goto and load page 0 - { - $page = 0; - $offset = 0; - } - } - - $sql.= " ".$db->plimit($conf->liste_limit+1, $offset); - - $resql = $db->query($sql); - if ($resql) - { - $num = $db->num_rows($resql); - $i = 0; - - $titre=$langs->trans("AssetsList"); - if ($status != '') - { - if ($status == '-1,1') { $titre=$langs->trans("MembersListQualified"); } - else if ($status == '-1') { $titre=$langs->trans("MembersListToValid"); } - else if ($status == '1' && ! $filter) { $titre=$langs->trans("MembersListValid"); } - else if ($status == '1' && $filter=='uptodate') { $titre=$langs->trans("MembersListUpToDate"); } - else if ($status == '1' && $filter=='outofdate') { $titre=$langs->trans("MembersListNotUpToDate"); } - else if ($status == '0') { $titre=$langs->trans("MembersListResiliated"); } - } - elseif ($action == 'search') - { - $titre=$langs->trans("MembersListQualified"); - } - - if ($type > 0) - { - $assettype=new AssetType($db); - $result=$assettype->fetch($type); - $titre.=" (".$assettype->label.")"; - } - - $param="&rowid=".$object->id; - if (! empty($status)) $param.="&status=".$status; - if (! empty($search_lastname)) $param.="&search_lastname=".$search_lastname; - if (! empty($search_firstname)) $param.="&search_firstname=".$search_firstname; - if (! empty($search_login)) $param.="&search_login=".$search_login; - if (! empty($search_email)) $param.="&search_email=".$search_email; - if (! empty($filter)) $param.="&filter=".$filter; - - if ($sall) - { - print $langs->trans("Filter")." (".$langs->trans("Lastname").", ".$langs->trans("Firstname").", ".$langs->trans("EMail").", ".$langs->trans("Address")." ".$langs->trans("or")." ".$langs->trans("Town")."): ".$sall; - } - - print '
'; - print '
'."\n"; - - // Lignes des champs de filtre - print ''; - - print ''; - - print ''; - - print ''; - - print ''; - - print ''; - - print ''; - - print "\n"; - - print ''; - print_liste_field_titre( $langs->trans("Name")." / ".$langs->trans("Company"),$_SERVER["PHP_SELF"],"d.lastname",$param,"","",$sortfield,$sortorder); - print_liste_field_titre("Login",$_SERVER["PHP_SELF"],"d.login",$param,"","",$sortfield,$sortorder); - print_liste_field_titre("Nature",$_SERVER["PHP_SELF"],"d.morphy",$param,"","",$sortfield,$sortorder); - print_liste_field_titre("EMail",$_SERVER["PHP_SELF"],"d.email",$param,"","",$sortfield,$sortorder); - print_liste_field_titre("Status",$_SERVER["PHP_SELF"],"d.statut,d.datefin",$param,"","",$sortfield,$sortorder); - print_liste_field_titre("EndSubscription",$_SERVER["PHP_SELF"],"d.datefin",$param,"",'align="center"',$sortfield,$sortorder); - print_liste_field_titre("Action",$_SERVER["PHP_SELF"],"",$param,"",'width="60" align="center"',$sortfield,$sortorder); - print "\n"; - - while ($i < $num && $i < $conf->liste_limit) - { - $objp = $db->fetch_object($resql); - - $datefin=$db->jdate($objp->datefin); - - $adh=new Asset($db); - $adh->lastname=$objp->lastname; - $adh->firstname=$objp->firstname; - - // Lastname - print ''; - if ($objp->societe != '') - { - print ''."\n"; - } - else - { - print ''."\n"; - } - - // Login - print "\n"; - - // Type - /*print ''; - */ - - // Moral/Physique - print "\n"; - - // EMail - print "\n"; - - // Statut - print '"; - - // Actions - print '"; - - print "\n"; - $i++; - } - - print "
'; - print ''; - print ' '; - print ' '; - print ''; - print '  '; - print ''; - print '
'.img_object($langs->trans("ShowMember"),"user").' '.$adh->getFullName($langs,0,-1,20).' / '.dol_trunc($objp->societe,12).''.img_object($langs->trans("ShowMember"),"user").' '.$adh->getFullName($langs,0,-1,32).'".$objp->login."'; - $assettypestatic->id=$objp->type_id; - $assettypestatic->label=$objp->type; - print $assettypestatic->getNomUrl(1,12); - print '".$adh->getmorphylib($objp->morphy)."".dol_print_email($objp->email,0,0,1)."'; - print $adh->LibStatut($objp->statut,$objp->subscription,$datefin,2); - print "'; - if ($user->rights->asset->creer) - { - print ''.img_edit().''; - } - print ' '; - if ($user->rights->asset->supprimer) - { - print ''.img_picto($langs->trans("Resiliate"),'disable.png').''; - } - print "
\n"; - print ''; - print ''; - - if ($num > $conf->liste_limit) - { - print_barre_liste('',$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$nbtotalofrecords,''); - } - } - else - { - dol_print_error($db); - } } /* ************************************************************************** */ diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php index af739838c10..1c42945fbe7 100644 --- a/htdocs/comm/action/class/actioncomm.class.php +++ b/htdocs/comm/action/class/actioncomm.class.php @@ -1679,13 +1679,19 @@ class ActionComm extends CommonObject $this->output = ''; $this->error=''; - if (empty($conf->global->AGENDA_REMINDER_EMAIL)) + if (empty($conf->agenda->enabled)) // Should not happen. If module disabled, cron job should not be visible. + { + $langs->load("agenda"); + $this->output = $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv("Agenda")); + return 0; + } + if (empty($conf->global->AGENDA_REMINDER_EMAIL)) { $langs->load("agenda"); $this->output = $langs->trans('EventRemindersByEmailNotEnabled', $langs->transnoentitiesnoconv("Agenda")); return 0; } - + $now = dol_now(); dol_syslog(__METHOD__, LOG_DEBUG); diff --git a/htdocs/comm/action/list.php b/htdocs/comm/action/list.php index b03493a4148..63a6a78ee8c 100644 --- a/htdocs/comm/action/list.php +++ b/htdocs/comm/action/list.php @@ -74,10 +74,14 @@ $filtert = GETPOST("filtert","int",3); $usergroup = GETPOST("usergroup","int",3); $showbirthday = empty($conf->use_javascript_ajax)?GETPOST("showbirthday","int"):1; +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$object = new ActionComm($db); +$hookmanager->initHooks(array('agendalist')); + $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('actioncomm'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // If not choice done on calendar owner, we filter on user. if (empty($filtert) && empty($conf->global->AGENDA_ALL_CALENDARS)) { @@ -117,10 +121,6 @@ if (! $user->rights->agenda->allactions->read || $filter=='mine') // If no permi $filtert=$user->id; } -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$object = new ActionComm($db); -$hookmanager->initHooks(array('agendalist')); - $arrayfields=array( 'a.id'=>array('label'=>"Ref", 'checked'=>1), 'owner'=>array('label'=>"Owner", 'checked'=>1), diff --git a/htdocs/comm/mailing/card.php b/htdocs/comm/mailing/card.php index d1f15f708f8..a2a6f743db8 100644 --- a/htdocs/comm/mailing/card.php +++ b/htdocs/comm/mailing/card.php @@ -747,7 +747,7 @@ if ($action == 'create') dol_fiche_head(); print ''; - print ''; + print ''; print ''; print ''; diff --git a/htdocs/comm/mailing/list.php b/htdocs/comm/mailing/list.php index 7a4edaa68aa..9f0cabea76f 100644 --- a/htdocs/comm/mailing/list.php +++ b/htdocs/comm/mailing/list.php @@ -46,20 +46,21 @@ $search_all=trim((GETPOST('search_all', 'alphanohtml')!='')?GETPOST('search_all' $search_ref=GETPOST("search_ref", "alpha") ? GETPOST("search_ref", "alpha") : GETPOST("sref", "alpha"); $filteremail=GETPOST('filteremail','alpha'); +$object = new Mailing($db); + // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('mailinglist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('mailing'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( 'm.titre'=>'Ref', ); -$object = new Mailing($db); diff --git a/htdocs/comm/propal/list.php b/htdocs/comm/propal/list.php index f9bbfee6302..89b6211d644 100644 --- a/htdocs/comm/propal/list.php +++ b/htdocs/comm/propal/list.php @@ -129,7 +129,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('propal'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/comm/remx.php b/htdocs/comm/remx.php index 78e4643d5e3..c3d907df45f 100644 --- a/htdocs/comm/remx.php +++ b/htdocs/comm/remx.php @@ -254,7 +254,7 @@ if ($socid > 0) print '
'; print '
'; - + if(! $isCustomer && ! $isSupplier) { print '

'.$langs->trans('ThirdpartyIsNeitherCustomerNorClientSoCannotHaveDiscounts').'

'; @@ -266,8 +266,8 @@ if ($socid > 0) $db->close(); exit; } - - + + print '
'.$langs->trans("MailTitle").'
'.$langs->trans("MailTitle").'
'.$langs->trans("MailFrom").'
'.$langs->trans("MailErrorsTo").'
'; if($isCustomer) { // Calcul avoirs client en cours @@ -293,7 +293,7 @@ if ($socid > 0) print ''; print ''; - + if (! empty($user->fk_soc)) // No need to show this for external users { print ''; @@ -322,10 +322,10 @@ if ($socid > 0) { dol_print_error($db); } - + print ''; print ''; - + if (! empty($user->fk_soc)) // No need to show this for external users { print ''; @@ -344,11 +344,11 @@ if ($socid > 0) print load_fiche_titre($langs->trans("NewGlobalDiscount"),'',''); print '
'; - + if($isCustomer && ! $isSupplier) { print ''; } - + if(! $isCustomer && $isSupplier) { print ''; } @@ -356,8 +356,8 @@ if ($socid > 0) print '
'.$langs->trans("CustomerAbsoluteDiscountAllUsers").''.$remise_all.' '.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'
'.$langs->trans("CustomerAbsoluteDiscountMy").'
'.$langs->trans("SupplierAbsoluteDiscountAllUsers").''.$remise_all.' '.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'
'.$langs->trans("SupplierAbsoluteDiscountMy").'
'; if($isCustomer && $isSupplier) { print ''; - print ''; } print ''; @@ -401,7 +401,7 @@ if ($socid > 0) /* * Liste remises fixes client restant en cours (= liees a aucune facture ni ligne de facture) */ - + print load_fiche_titre($langs->trans("DiscountStillRemaining")); if($isCustomer) { @@ -424,10 +424,11 @@ if ($socid > 0) $sql.= " AND rc.discount_type = 0"; // Eliminate supplier discounts $sql.= " AND (rc.fk_facture_line IS NULL AND rc.fk_facture IS NULL)"; $sql.= " ORDER BY rc.datec DESC"; - + $resql=$db->query($sql); if ($resql) { + print '
'; print '
'.$langs->trans('DiscountType').' '; - print ' '; + print ' '; + print '   '; print '
'.$langs->trans("AmountHT").'
'; print ''; print ''; // Need 120+ for format with AM/PM @@ -439,9 +440,9 @@ if ($socid > 0) print ''; print ''; print ''; - + $showconfirminfo=array(); - + $i = 0; $num = $db->num_rows($resql); if ($num > 0) @@ -449,7 +450,7 @@ if ($socid > 0) while ($i < $num) { $obj = $db->fetch_object($resql); - + print ''; print ''; if (preg_match('/\(CREDIT_NOTE\)/',$obj->description)) @@ -502,7 +503,7 @@ if ($socid > 0) } else print ''; print ''; - + if ($_GET["action"]=='split' && GETPOST('remid') == $obj->rowid) { $showconfirminfo['rowid']=$obj->rowid; @@ -517,7 +518,8 @@ if ($socid > 0) } $db->free($resql); print "
'.$langs->trans("Date").''.$langs->trans("DiscountOfferedBy").' 
'.dol_print_date($db->jdate($obj->dc),'dayhour').' 
"; - + print ''; + if (count($showconfirminfo)) { $amount1=price2num($showconfirminfo['amount_ttc']/2,'MT'); @@ -561,10 +563,11 @@ if ($socid > 0) $sql.= " AND rc.discount_type = 1"; // Eliminate customer discounts $sql.= " AND (rc.fk_invoice_supplier IS NULL AND rc.fk_invoice_supplier_line IS NULL)"; $sql.= " ORDER BY rc.datec DESC"; - + $resql=$db->query($sql); if ($resql) { + print '
'; print ''; print ''; print ''; // Need 120+ for format with AM/PM @@ -576,9 +579,9 @@ if ($socid > 0) print ''; print ''; print ''; - + $showconfirminfo=array(); - + $i = 0; $num = $db->num_rows($resql); if ($num > 0) @@ -586,7 +589,7 @@ if ($socid > 0) while ($i < $num) { $obj = $db->fetch_object($resql); - + print ''; print ''; if (preg_match('/\(CREDIT_NOTE\)/',$obj->description)) @@ -639,7 +642,7 @@ if ($socid > 0) } else print ''; print ''; - + if ($_GET["action"]=='split' && GETPOST('remid') == $obj->rowid) { $showconfirminfo['rowid']=$obj->rowid; @@ -654,7 +657,8 @@ if ($socid > 0) } $db->free($resql); print "
'.$langs->trans("Date").''.$langs->trans("DiscountOfferedBy").' 
'.dol_print_date($db->jdate($obj->dc),'dayhour').' 
"; - + print '
'; + if (count($showconfirminfo)) { $amount1=price2num($showconfirminfo['amount_ttc']/2,'MT'); @@ -673,19 +677,19 @@ if ($socid > 0) dol_print_error($db); } - if($isCustomer) { + if ($isCustomer) { print ''; // class="ficheaddleft" print ''; // class="fichehalfright" print ''; // class="fichecenter" } } - print '
'; + print '

'; /* * List discount consumed (=liees a une ligne de facture ou facture) */ - + print load_fiche_titre($langs->trans("DiscountAlreadyCounted")); if($isCustomer) { @@ -730,12 +734,13 @@ if ($socid > 0) $sql2.= " AND rc.fk_user = u.rowid"; $sql2.= " AND rc.discount_type = 0"; // Eliminate supplier discounts $sql2.= " ORDER BY dc DESC"; - + $resql=$db->query($sql); $resql2=null; if ($resql) $resql2=$db->query($sql2); if ($resql2) { + print '
'; print ''; print ''; print ''; // Need 120+ for format with AM/PM @@ -747,7 +752,7 @@ if ($socid > 0) print ''; print ''; print ''; - + $tab_sqlobj=array(); $tab_sqlobjOrder=array(); $num = $db->num_rows($resql); @@ -761,7 +766,7 @@ if ($socid > 0) } } $db->free($resql); - + $num = $db->num_rows($resql2); for ($i = 0;$i < $num;$i++) { @@ -771,7 +776,7 @@ if ($socid > 0) } $db->free($resql2); array_multisort($tab_sqlobjOrder,SORT_DESC,$tab_sqlobj); - + $num = count($tab_sqlobj); if ($num > 0) { @@ -830,8 +835,9 @@ if ($socid > 0) { print ''; } - + print "
'.$langs->trans("Date").''.$langs->trans("Author").' 
'.$langs->trans("None").'
"; + print '
'; } else { @@ -882,12 +888,13 @@ if ($socid > 0) $sql2.= " AND rc.fk_user = u.rowid"; $sql2.= " AND rc.discount_type = 1"; // Eliminate customer discounts $sql2.= " ORDER BY dc DESC"; - + $resql=$db->query($sql); $resql2=null; if ($resql) $resql2=$db->query($sql2); if ($resql2) { + print '
'; print ''; print ''; print ''; // Need 120+ for format with AM/PM @@ -899,7 +906,7 @@ if ($socid > 0) print ''; print ''; print ''; - + $tab_sqlobj=array(); $tab_sqlobjOrder=array(); $num = $db->num_rows($resql); @@ -913,7 +920,7 @@ if ($socid > 0) } } $db->free($resql); - + $num = $db->num_rows($resql2); for ($i = 0;$i < $num;$i++) { @@ -923,7 +930,7 @@ if ($socid > 0) } $db->free($resql2); array_multisort($tab_sqlobjOrder,SORT_DESC,$tab_sqlobj); - + $num = count($tab_sqlobj); if ($num > 0) { @@ -982,8 +989,9 @@ if ($socid > 0) { print ''; } - + print "
'.$langs->trans("Date").''.$langs->trans("Author").' 
'.$langs->trans("None").'
"; + print '
'; } else { diff --git a/htdocs/commande/list.php b/htdocs/commande/list.php index f434cbddbdb..a6acfcfd029 100644 --- a/htdocs/commande/list.php +++ b/htdocs/commande/list.php @@ -108,7 +108,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('commande'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( @@ -466,7 +466,9 @@ if ($resql) print ''; print ''; print ''; - + print ''; + + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, $newcardbutton, '', $limit); $topicmail="SendOrderRef"; diff --git a/htdocs/compta/bank/bankentries_list.php b/htdocs/compta/bank/bankentries_list.php index 22513997c00..94885ab118b 100644 --- a/htdocs/compta/bank/bankentries_list.php +++ b/htdocs/compta/bank/bankentries_list.php @@ -129,7 +129,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('banktransaction'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost('banktransaction','','search_'); $arrayfields=array( 'b.rowid'=>array('label'=>$langs->trans("Ref"), 'checked'=>1), diff --git a/htdocs/compta/bank/list.php b/htdocs/compta/bank/list.php index 8758ccb98a6..2395a3c23de 100644 --- a/htdocs/compta/bank/list.php +++ b/htdocs/compta/bank/list.php @@ -74,7 +74,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('bank_account'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 951762faadb..2414afd3b97 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -1357,6 +1357,8 @@ if (empty($reshook)) { // Don't add lines with qty 0 when coming from a shipment including all order lines if($srcobject->element == 'shipping' && $conf->global->SHIPMENT_GETS_ALL_ORDER_PRODUCTS && $lines[$i]->qty == 0) continue; + // Don't add closed lines when coming from a contract + if($srcobject->element == 'contrat' && $lines[$i]->statut == 5) continue; $label=(! empty($lines[$i]->label)?$lines[$i]->label:''); $desc=(! empty($lines[$i]->desc)?$lines[$i]->desc:$lines[$i]->libelle); diff --git a/htdocs/compta/facture/fiche-rec.php b/htdocs/compta/facture/fiche-rec.php index 0ce812b2355..856433173ad 100644 --- a/htdocs/compta/facture/fiche-rec.php +++ b/htdocs/compta/facture/fiche-rec.php @@ -94,7 +94,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('facture_rec'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); $permissionnote = $user->rights->facture->creer; // Used by the include of actions_setnotes.inc.php $permissiondellink=$user->rights->facture->creer; // Used by the include of actions_dellink.inc.php diff --git a/htdocs/compta/facture/invoicetemplate_list.php b/htdocs/compta/facture/invoicetemplate_list.php index e83932e0b9b..c0a80c86b74 100644 --- a/htdocs/compta/facture/invoicetemplate_list.php +++ b/htdocs/compta/facture/invoicetemplate_list.php @@ -108,7 +108,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('facture_rec'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); $permissionnote = $user->rights->facture->creer; // Used by the include of actions_setnotes.inc.php $permissiondellink=$user->rights->facture->creer; // Used by the include of actions_dellink.inc.php diff --git a/htdocs/compta/facture/list.php b/htdocs/compta/facture/list.php index effaaee6a63..83925a38b08 100644 --- a/htdocs/compta/facture/list.php +++ b/htdocs/compta/facture/list.php @@ -134,7 +134,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('facture'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/compta/index.php b/htdocs/compta/index.php index f6049d315e9..ac6479afc0b 100644 --- a/htdocs/compta/index.php +++ b/htdocs/compta/index.php @@ -636,7 +636,7 @@ if (! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) print ''; print ''; - print ''; + print ''; print ''; print ''; print ''; diff --git a/htdocs/compta/resultat/clientfourn.php b/htdocs/compta/resultat/clientfourn.php index e5ecf562c1d..e08ebb17ed1 100644 --- a/htdocs/compta/resultat/clientfourn.php +++ b/htdocs/compta/resultat/clientfourn.php @@ -773,7 +773,7 @@ else print ''; - print "\n"; + print "\n"; if ($modecompta == 'CREANCES-DETTES') print ''; print ''; diff --git a/htdocs/compta/salaries/card.php b/htdocs/compta/salaries/card.php index f50d940a51a..76ab712bffd 100644 --- a/htdocs/compta/salaries/card.php +++ b/htdocs/compta/salaries/card.php @@ -174,7 +174,7 @@ if ($action == 'delete') if ($result >= 0) { $db->commit(); - header("Location: ".DOL_URL_ROOT.'/compta/salaries/index.php'); + header("Location: ".DOL_URL_ROOT.'/compta/salaries/list.php'); exit; } else @@ -365,7 +365,7 @@ if ($id) dol_fiche_head($head, 'card', $langs->trans("SalaryPayment"), -1, 'payment'); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; $morehtmlref='
'; diff --git a/htdocs/compta/salaries/class/paymentsalary.class.php b/htdocs/compta/salaries/class/paymentsalary.class.php index 0be0f4f3694..76a8284daff 100644 --- a/htdocs/compta/salaries/class/paymentsalary.class.php +++ b/htdocs/compta/salaries/class/paymentsalary.class.php @@ -537,24 +537,73 @@ class PaymentSalary extends CommonObject /** * Send name clicable (with possibly the picto) * - * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto - * @param string $option link option - * @return string Chaine with URL + * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto + * @param string $option link option + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string Chaine with URL */ - function getNomUrl($withpicto=0,$option='') + function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1) { - global $langs; + global $db, $conf, $langs, $hookmanager; + global $dolibarr_main_authentication, $dolibarr_main_demo; + global $menumanager; - $result=''; - $label=$langs->trans("ShowSalaryPayment").': '.$this->ref; + if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips - $linkstart = ''; + $result = ''; + + $label = '' . $langs->trans("ShowSalaryPayment") . ''; + $label.= '
'; + $label.= '' . $langs->trans('Ref') . ': ' . $this->ref; + + $url = DOL_URL_ROOT.'/compta/salaries/card.php?id='.$this->id; + + if ($option != 'nolink') + { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; + if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + } + + $linkclose=''; + if (empty($notooltip)) + { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowMyObject"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; + + /* + $hookmanager->initHooks(array('myobjectdao')); + $parameters=array('id'=>$this->id); + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $linkclose = $hookmanager->resPrint; + */ + } + else $linkclose = ($morecss?' class="'.$morecss.'"':''); + + $linkstart = '
'; $linkend=''; $result .= $linkstart; if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); if ($withpicto != 2) $result.= $this->ref; $result .= $linkend; + //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + global $action,$hookmanager; + $hookmanager->initHooks(array('salarypayment')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; return $result; } diff --git a/htdocs/compta/salaries/document.php b/htdocs/compta/salaries/document.php index 06d5889c44e..33e92ecaf3b 100644 --- a/htdocs/compta/salaries/document.php +++ b/htdocs/compta/salaries/document.php @@ -99,7 +99,7 @@ if ($object->id) $totalsize+=$file['size']; } - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; $morehtmlref='
'; diff --git a/htdocs/compta/salaries/info.php b/htdocs/compta/salaries/info.php index d2f02f3dc8f..87d24ceb406 100644 --- a/htdocs/compta/salaries/info.php +++ b/htdocs/compta/salaries/info.php @@ -54,7 +54,7 @@ $head = salaries_prepare_head($object); dol_fiche_head($head, 'info', $langs->trans("SalaryPayment"), -1, 'payment'); -$linkback = ''.$langs->trans("BackToList").''; +$linkback = ''.$langs->trans("BackToList").''; $morehtmlref='
'; diff --git a/htdocs/compta/salaries/index.php b/htdocs/compta/salaries/list.php similarity index 99% rename from htdocs/compta/salaries/index.php rename to htdocs/compta/salaries/list.php index a8a0b82e7d9..c7f5970723e 100644 --- a/htdocs/compta/salaries/index.php +++ b/htdocs/compta/salaries/list.php @@ -18,7 +18,7 @@ */ /** - * \file htdocs/compta/salaries/index.php + * \file htdocs/compta/salaries/list.php * \ingroup salaries * \brief List of salaries payments */ @@ -231,6 +231,7 @@ if ($result) $salstatic->id=$obj->rowid; $salstatic->ref=$obj->rowid; + // Ref print "
\n"; // Employee diff --git a/htdocs/compta/sociales/card.php b/htdocs/compta/sociales/card.php index a6c41d8c431..6a0d53d6987 100644 --- a/htdocs/compta/sociales/card.php +++ b/htdocs/compta/sociales/card.php @@ -481,7 +481,7 @@ if ($id > 0) } $morehtmlref.=''; - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; $object->totalpaye = $totalpaye; // To give a chance to dol_banner_tab to use already paid amount to show correct status diff --git a/htdocs/compta/sociales/document.php b/htdocs/compta/sociales/document.php index ca81c11e5f1..6b5fd8f9e52 100644 --- a/htdocs/compta/sociales/document.php +++ b/htdocs/compta/sociales/document.php @@ -127,7 +127,7 @@ if ($object->id) } $morehtmlref.=''; - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; $object->totalpaye = $totalpaye; // To give a chance to dol_banner_tab to use already paid amount to show correct status diff --git a/htdocs/compta/sociales/info.php b/htdocs/compta/sociales/info.php index bd21db108c1..35c00766252 100644 --- a/htdocs/compta/sociales/info.php +++ b/htdocs/compta/sociales/info.php @@ -98,7 +98,7 @@ if (! empty($conf->projet->enabled)) } $morehtmlref.=''; -$linkback = '' . $langs->trans("BackToList") . ''; +$linkback = '' . $langs->trans("BackToList") . ''; $object->totalpaye = $totalpaye; // To give a chance to dol_banner_tab to use already paid amount to show correct status diff --git a/htdocs/compta/sociales/index.php b/htdocs/compta/sociales/list.php similarity index 92% rename from htdocs/compta/sociales/index.php rename to htdocs/compta/sociales/list.php index 0a0176cb4b3..636cb92978a 100644 --- a/htdocs/compta/sociales/index.php +++ b/htdocs/compta/sociales/list.php @@ -19,7 +19,7 @@ */ /** - * \file htdocs/compta/sociales/index.php + * \file htdocs/compta/list/index.php * \ingroup tax * \brief Page to list all social contributions */ @@ -55,19 +55,19 @@ if (! $sortorder) $sortorder="DESC"; $year=GETPOST("year",'int'); $filtre=GETPOST("filtre",'int'); -if (empty($_REQUEST['typeid'])) +if (! GETPOSTISSET('search_typeid')) { $newfiltre=str_replace('filtre=','',$filtre); $filterarray=explode('-',$newfiltre); foreach($filterarray as $val) { $part=explode(':',$val); - if ($part[0] == 'cs.fk_type') $typeid=$part[1]; + if ($part[0] == 'cs.fk_type') $search_typeid=$part[1]; } } else { - $typeid=$_REQUEST['typeid']; + $search_typeid=GETPOST('search_typeid','int'); } if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha')) // All test are required to be compatible with all browsers @@ -76,11 +76,12 @@ if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x', $search_label=""; $search_amount=""; $search_status=''; - $typeid=""; + $search_typeid=""; $year=""; $month=""; } + /* * View */ @@ -118,8 +119,8 @@ if ($filtre) { $filtre=str_replace(":","=",$filtre); $sql .= " AND ".$filtre; } -if ($typeid) { - $sql .= " AND cs.fk_type=".$db->escape($typeid); +if ($search_typeid) { + $sql .= " AND cs.fk_type=".$db->escape($search_typeid); } $sql.= " GROUP BY cs.rowid, cs.fk_type, cs.amount, cs.date_ech, cs.libelle, cs.paye, cs.periode, c.libelle"; $sql.= $db->order($sortfield,$sortorder); @@ -139,10 +140,14 @@ if ($resql) $i = 0; $param=''; - if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; - if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; - if ($year) $param.='&year='.$year; - if ($typeid) $param.='&typeid='.$typeid; + if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); + if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); + if ($search_ref) $param.='&search_ref='.urlencode($search_ref); + if ($search_label) $param.='&search_label='.urlencode($search_label); + if ($search_amount) $param.='&search_amount='.urlencode($search_amount); + if ($search_typeid) $param.='&search_typeid='.urlencode($search_typeid); + if ($search_status != '' && $search_status != '-1') $param.='&search_status='.urlencode($search_status); + if ($year) $param.='&year='.urlencode($year); $newcardbutton=''; if($user->rights->tax->charges->creer) @@ -193,7 +198,7 @@ if ($resql) print ''; // Type print ''; // Period end date print ''; diff --git a/htdocs/contact/list.php b/htdocs/contact/list.php index 4a101416886..637e1879d20 100644 --- a/htdocs/contact/list.php +++ b/htdocs/contact/list.php @@ -133,7 +133,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('contact'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( @@ -321,6 +321,7 @@ if (strlen($search_twitter)) $sql.= natural_search('p.twitter', $search_t if (strlen($search_facebook)) $sql.= natural_search('p.facebook', $search_facebook); if (strlen($search_email)) $sql.= natural_search('p.email', $search_email); if (strlen($search_zip)) $sql.= natural_search("p.zip",$search_zip); +if (strlen($search_town)) $sql.= natural_search("p.town",$search_town); if ($search_status != '' && $search_status >= 0) $sql.= " AND p.statut = ".$db->escape($search_status); if ($search_import_key) $sql.= natural_search("p.import_key",$search_import_key); if ($type == "o") // filtre sur type diff --git a/htdocs/contrat/class/contrat.class.php b/htdocs/contrat/class/contrat.class.php index 589cf7174e6..16e343aaba8 100644 --- a/htdocs/contrat/class/contrat.class.php +++ b/htdocs/contrat/class/contrat.class.php @@ -2581,7 +2581,7 @@ class ContratLigne extends CommonObjectLine /** * @var float * @deprecated Use $price_ht instead - * @see price_ht + * @see $price_ht */ public $price; diff --git a/htdocs/contrat/list.php b/htdocs/contrat/list.php index 3719e3618e6..056362bb2d2 100644 --- a/htdocs/contrat/list.php +++ b/htdocs/contrat/list.php @@ -100,7 +100,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('contrat'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( 'c.ref'=>'Ref', diff --git a/htdocs/contrat/services_list.php b/htdocs/contrat/services_list.php index a57bfd0f175..806bff48605 100644 --- a/htdocs/contrat/services_list.php +++ b/htdocs/contrat/services_list.php @@ -84,7 +84,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('contratdet'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Security check $contratid = GETPOST('id','int'); diff --git a/htdocs/core/actions_sendmails.inc.php b/htdocs/core/actions_sendmails.inc.php index 0715a78495e..61c4b636758 100644 --- a/htdocs/core/actions_sendmails.inc.php +++ b/htdocs/core/actions_sendmails.inc.php @@ -439,6 +439,10 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO $object->sendtouserid = $sendtouserid; } + // TODO Set object->email_xxx properties + $object->email_msgid = $mailfile->msgid; + //... + // Call of triggers if (! empty($trigger_name)) { diff --git a/htdocs/core/class/extrafields.class.php b/htdocs/core/class/extrafields.class.php index aaacee51b50..a52a759f841 100644 --- a/htdocs/core/class/extrafields.class.php +++ b/htdocs/core/class/extrafields.class.php @@ -1941,24 +1941,36 @@ class ExtraFields /** * return array_options array of data of extrafields value of object sent by a search form * - * @param array $extralabels $array of extrafields (@deprecated) - * @param string $keyprefix Prefix string to add into name and id of field (can be used to avoid duplicate names) - * @param string $keysuffix Suffix string to add into name and id of field (can be used to avoid duplicate names) - * @return array|int array_options set or 0 if no value + * @param array|string $extrafieldsobjectkey array of extrafields (old usage) or value of object->table_element (new usage) + * @param string $keyprefix Prefix string to add into name and id of field (can be used to avoid duplicate names) + * @param string $keysuffix Suffix string to add into name and id of field (can be used to avoid duplicate names) + * @return array|int array_options set or 0 if no value */ - function getOptionalsFromPost($extralabels,$keyprefix='',$keysuffix='') + function getOptionalsFromPost($extrafieldsobjectkey, $keyprefix='', $keysuffix='') { global $_POST; - if (is_array($this->attributes[$object->table_element]['label'])) $extralabels=$this->attributes[$object->table_element]['label']; + if (is_string($extrafieldsobjectkey) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) + { + $extralabels = $this->attributes[$extrafieldsobjectkey]['label']; + } + else + { + $extralabels = $extrafieldsobjectkey; + } - $array_options = array(); if (is_array($extralabels)) { + $array_options = array(); + // Get extra fields foreach ($extralabels as $key => $value) { - $key_type = $this->attributes[$object->table_element]['type'][$key]; + $key_type = ''; + if (is_string($extrafieldsobjectkey)) + { + $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key]; + } if (in_array($key_type,array('date','datetime'))) { @@ -1987,8 +1999,7 @@ class ExtraFields return $array_options; } - else { - return 0; - } + + return 0; } } diff --git a/htdocs/core/class/html.formfile.class.php b/htdocs/core/class/html.formfile.class.php index 63f4c2df804..09298fe1bff 100644 --- a/htdocs/core/class/html.formfile.class.php +++ b/htdocs/core/class/html.formfile.class.php @@ -132,6 +132,7 @@ class FormFile if ($maxmin > 0) { + // MAX_FILE_SIZE doit précéder le champ input de type file $out .= ''; } @@ -1017,7 +1018,7 @@ class FormFile global $form; $disablecrop=1; - if (in_array($modulepart, array('societe','product','produit','service','expensereport','holiday','member','project','ticket','user'))) $disablecrop=0; + if (in_array($modulepart, array('expensereport','holiday','member','project','product','produit','service','societe','tax','ticket','user'))) $disablecrop=0; // Define relative path used to store the file if (empty($relativepath)) @@ -1218,9 +1219,13 @@ class FormFile if (! dol_is_file($file['path'].'/'.$minifile)) $minifile=getImageFileNameForSize($file['name'], '_mini', '.png'); // For backward compatibility of old thumbs that were created with filename in lower case and with .png extension //print $file['path'].'/'.$minifile.'
'; - $urlforhref=getAdvancedPreviewUrl($modulepart, $relativepath.$fileinfo['filename'].'.'.strtolower($fileinfo['extension']), 0, '&entity='.(!empty($object->entity)?$object->entity:$conf->entity)); - if (empty($urlforhref)) $urlforhref=DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.(!empty($object->entity)?$object->entity:$conf->entity).'&file='.urlencode($relativepath.$fileinfo['filename'].'.'.strtolower($fileinfo['extension'])); - print ''; + $urlforhref=getAdvancedPreviewUrl($modulepart, $relativepath.$fileinfo['filename'].'.'.strtolower($fileinfo['extension']), 1, '&entity='.(!empty($object->entity)?$object->entity:$conf->entity)); + if (empty($urlforhref)) { + $urlforhref=DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.(!empty($object->entity)?$object->entity:$conf->entity).'&file='.urlencode($relativepath.$fileinfo['filename'].'.'.strtolower($fileinfo['extension'])); + print ''; + } else { + print ''; + } print ''; print ''; } diff --git a/htdocs/core/class/html.formprojet.class.php b/htdocs/core/class/html.formprojet.class.php index bc62ebc7604..1b6efe56820 100644 --- a/htdocs/core/class/html.formprojet.class.php +++ b/htdocs/core/class/html.formprojet.class.php @@ -317,14 +317,20 @@ class FormProjets * @param string $morecss More css added to the select component * @param string $projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids. * @param string $showproject 'all' = Show project info, ''=Hide project info + * @param User $usertofilter User object to use for filtering * @return int Nbr of project if OK, <0 if KO */ - function selectTasks($socid=-1, $selected='', $htmlname='taskid', $maxlength=24, $option_only=0, $show_empty='1', $discard_closed=0, $forcefocus=0, $disabled=0, $morecss='maxwidth500', $projectsListId='', $showproject='all') + function selectTasks($socid=-1, $selected='', $htmlname='taskid', $maxlength=24, $option_only=0, $show_empty='1', $discard_closed=0, $forcefocus=0, $disabled=0, $morecss='maxwidth500', $projectsListId='', $showproject='all', $usertofilter=null) { global $user,$conf,$langs; require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; + if (is_null($usertofilter)) + { + $usertofilter = $user; + } + $out=''; $hideunselectables = false; @@ -332,10 +338,10 @@ class FormProjets if (empty($projectsListId)) { - if (empty($user->rights->projet->all->lire)) + if (empty($usertofilter->rights->projet->all->lire)) { $projectstatic=new Project($this->db); - $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user,0,1); + $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter,0,1); } } @@ -381,7 +387,7 @@ class FormProjets { $obj = $this->db->fetch_object($resql); // If we ask to filter on a company and user has no permission to see all companies and project is linked to another company, we hide project. - if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($user->rights->societe->lire)) + if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire)) { // Do nothing } @@ -648,7 +654,7 @@ class FormProjets $sellist .= '>'; if ($useshortlabel) { - $finallabel = ($langs->transnoentitiesnoconv("OppStatusShort".$obj->code) != "OppStatusShort".$obj->code ? $langs->transnoentitiesnoconv("OppStatusShort".$obj->code) : $obj->label); + $finallabel = ($langs->transnoentitiesnoconv("OppStatus".$obj->code) != "OppStatus".$obj->code ? $langs->transnoentitiesnoconv("OppStatus".$obj->code) : $obj->label); } else { diff --git a/htdocs/core/class/notify.class.php b/htdocs/core/class/notify.class.php index 18635aeee00..e7cd7cabe53 100644 --- a/htdocs/core/class/notify.class.php +++ b/htdocs/core/class/notify.class.php @@ -631,13 +631,13 @@ class Notify $mesg = $langs->transnoentitiesnoconv("EMailTextOrderValidated",$link); break; case 'PROPAL_VALIDATE': - $link='' . $newref . ''; + $link = '' . $newref . ''; $dir_output = $conf->propal->multidir_output[$object->entity]; $object_type = 'propal'; $mesg = $langs->transnoentitiesnoconv("EMailTextProposalValidated",$link); break; case 'PROPAL_CLOSE_SIGNED': - $link='' . $newref . ''; + $link = '' . $newref . ''; $dir_output = $conf->propal->multidir_output[$object->entity]; $object_type = 'propal'; $mesg = $langs->transnoentitiesnoconv("EMailTextProposalClosedSigned",$link); diff --git a/htdocs/core/class/translate.class.php b/htdocs/core/class/translate.class.php index 756dbade364..599bf25d33d 100644 --- a/htdocs/core/class/translate.class.php +++ b/htdocs/core/class/translate.class.php @@ -562,10 +562,6 @@ class Translate elseif (preg_match('/^PaymentTypeShort([0-9A-Z]+)$/i',$key,$reg)) { $newstr=$this->getLabelFromKey($db,$reg[1],'c_paiement','code','libelle','',1); - } - elseif (preg_match('/^OppStatusShort([0-9A-Z]+)$/i',$key,$reg)) - { - $newstr=$this->getLabelFromKey($db,$reg[1],'c_lead_status','code','label'); } elseif (preg_match('/^OppStatus([0-9A-Z]+)$/i',$key,$reg)) { @@ -577,7 +573,11 @@ class Translate //$newstr=$this->getLabelFromKey($db,$reg[1],'c_ordersource','code','label'); } - if (! empty($conf->global->MAIN_FEATURES_LEVEL) && $conf->global->MAIN_FEATURES_LEVEL >= 2) dol_syslog(__METHOD__." missing translation for key '".$newstr."' in ".$_SERVER["PHP_SELF"], LOG_DEBUG); + /* Disabled. There is too many cases where translation of $newstr is not defined is normal (like when output with setEventMessage an already translated string) + if (! empty($conf->global->MAIN_FEATURES_LEVEL) && $conf->global->MAIN_FEATURES_LEVEL >= 2) + { + dol_syslog(__METHOD__." MAIN_FEATURES_LEVEL=DEVELOP: missing translation for key '".$newstr."' in ".$_SERVER["PHP_SELF"], LOG_DEBUG); + }*/ return $newstr; } diff --git a/htdocs/core/js/lib_head.js.php b/htdocs/core/js/lib_head.js.php index ea19bc258be..15594570fd7 100644 --- a/htdocs/core/js/lib_head.js.php +++ b/htdocs/core/js/lib_head.js.php @@ -918,7 +918,7 @@ function document_preview(file, type, title) { optionsbuttons = { "transnoentitiesnoconv("OriginalSize")); ?>": function() { console.log("Click on original size"); jQuery(".ui-dialog-content.ui-widget-content > object").css({ "max-height": "none" }); }, - "transnoentitiesnoconv("Close")); ?>": function() { $( this ).dialog( "close" ); } + "transnoentitiesnoconv("CloseWindow")); ?>": function() { $( this ).dialog( "close" ); } }; } diff --git a/htdocs/core/lib/admin.lib.php b/htdocs/core/lib/admin.lib.php index 29c8ab08718..0c9a43d5ca9 100644 --- a/htdocs/core/lib/admin.lib.php +++ b/htdocs/core/lib/admin.lib.php @@ -944,7 +944,6 @@ function activateModule($value,$withdeps=1) $activate = false; foreach ($modulesdir as $dir) { - var_dump($modulestring); if (file_exists($dir.$modulestring.".class.php")) { $resarray = activateModule($modulestring); @@ -1354,7 +1353,8 @@ function complete_elementList_with_modules(&$elementList) /** * Show array with constants to edit * - * @param array $tableau Array of constants array('key'=>type, ) where type can be 'string', 'text', 'textarea', 'html', 'yesno', 'emailtemplate:xxx', ... + * @param array $tableau Array of constants array('key'=>array('type'=>type, 'label'=>label) + * where type can be 'string', 'text', 'textarea', 'html', 'yesno', 'emailtemplate:xxx', ... * @param int $strictw3c 0=Include form into table (deprecated), 1=Form is outside table to respect W3C (no form into table), 2=No form nor button at all * @param string $helptext Help * @return void @@ -1378,17 +1378,28 @@ function form_constantes($tableau, $strictw3c=0, $helptext='') if (empty($strictw3c)) print ''; print "\n"; + $label=''; $listofparam=array(); foreach($tableau as $key => $const) // Loop on each param { + $label=''; // $const is a const key like 'MYMODULE_ABC' - if (is_numeric($key)) { + if (is_numeric($key)) { // Very old behaviour $type = 'string'; } else { - $type = $const; - $const = $key; + if (is_array($const)) + { + $type = $const['type']; + $label = $const['label']; + $const = $key; + } + else + { + $type = $const; + $const = $key; + } } $sql = "SELECT "; @@ -1429,7 +1440,7 @@ function form_constantes($tableau, $strictw3c=0, $helptext='') print ''; print ''; - print $langs->trans('Desc'.$const); + print ($label ? $label : $langs->trans('Desc'.$const)); if ($const == 'ADHERENT_MAILMAN_URL') { diff --git a/htdocs/core/lib/company.lib.php b/htdocs/core/lib/company.lib.php index 6193d764e43..febd4fcb703 100644 --- a/htdocs/core/lib/company.lib.php +++ b/htdocs/core/lib/company.lib.php @@ -120,6 +120,7 @@ function societe_prepare_head(Societe $object) $sql = "SELECT COUNT(n.rowid) as nb"; $sql.= " FROM ".MAIN_DB_PREFIX."projet as n"; $sql.= " WHERE fk_soc = ".$object->id; + $sql.= " AND entity IN (".getEntity('project').")"; $resql=$db->query($sql); if ($resql) { @@ -739,11 +740,12 @@ function show_projects($conf, $langs, $db, $object, $backtopage='', $nocreatelin print '
'; print "\n".'
'.$langs->trans("ContributionsToPay").($num?' '.$num.'':'').''.$langs->trans("ContributionsToPay").($num?' '.$num.'':'').''.$langs->trans("DateDue").''.$langs->trans("AmountTTC").''.$langs->trans("Paid").'
 ".$langs->trans("Salary")." fk_user."\">".$obj->firstname." ".$obj->lastname."".$langs->trans("Salary")." fk_user."\">".$obj->firstname." ".$obj->lastname."'.price(-$obj->amount).''.price(-$obj->amount).'".$salstatic->getNomUrl(1)."'; - $formsocialcontrib->select_type_socialcontrib($typeid,'typeid',1,0,0,'maxwidth100onsmartphone'); + $formsocialcontrib->select_type_socialcontrib($search_typeid,'search_typeid',1,0,0,'maxwidth100onsmartphone'); print ' '.$langs->trans("Action").'
'; - $sql = "SELECT p.rowid as id, p.title, p.ref, p.public, p.dateo as do, p.datee as de, p.fk_statut as status, p.fk_opp_status, p.opp_amount, p.opp_percent, p.tms as date_update, p.budget_amount"; + $sql = "SELECT p.rowid as id, p.entity, p.title, p.ref, p.public, p.dateo as do, p.datee as de, p.fk_statut as status, p.fk_opp_status, p.opp_amount, p.opp_percent, p.tms as date_update, p.budget_amount"; $sql .= ", cls.code as opp_status_code"; $sql .= " FROM ".MAIN_DB_PREFIX."projet as p"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_lead_status as cls on p.fk_opp_status = cls.rowid"; $sql .= " WHERE p.fk_soc = ".$object->id; + $sql .= " AND p.entity IN (".getEntity('project').")"; $sql .= " ORDER BY p.dateo DESC"; $result=$db->query($sql); @@ -799,7 +801,7 @@ function show_projects($conf, $langs, $db, $object, $backtopage='', $nocreatelin print ''; // Opp status print ''; // Opp percent print ''; print 'fields as $key => $val) print '"'; print '>'; print $langs->trans($val['label']); + if(!empty($val['help'])){ + print $form->textwithpicto('',$langs->trans($val['help'])); + } print ''; print '
'; - if ($obj->opp_status_code) print $langs->trans("OppStatusShort".$obj->opp_status_code); + if ($obj->opp_status_code) print $langs->trans("OppStatus".$obj->opp_status_code); print ''; @@ -908,7 +910,7 @@ function show_contacts($conf,$langs,$db,$object,$backtopage='') { if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha'); } - $search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); + $search_array_options=$extrafields->getOptionalsFromPost($contactstatic->table_element,'','search_'); // Purge search criteria if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index b911462a0c9..5ec9e40518f 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -668,7 +668,7 @@ function dol_include_once($relpath, $classname='') /** - * Return path of url or filesystem. Return alternate root if exists. + * Return path of url or filesystem. Can check into alternate dir or alternate dir + main dir depending on value of $returnemptyifnotfound. * * @param string $path Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile * @param int $type 0=Used for a Filesystem path, 1=Used for an URL path (output relative), 2=Used for an URL path (output full path using same host that current url), 3=Used for an URL path (output full path using host defined into $dolibarr_main_url_root of conf file) @@ -688,7 +688,10 @@ function dol_buildpath($path, $type=0, $returnemptyifnotfound=0) $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path foreach ($conf->file->dol_document_root as $key => $dirroot) // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...) { - if ($key == 'main') continue; + if ($key == 'main') + { + continue; + } if (file_exists($dirroot.'/'.$path)) { $res=$dirroot.'/'.$path; @@ -1035,6 +1038,8 @@ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename=' } if ($level > $conf->global->SYSLOG_LEVEL) return; + $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log + // If adding log inside HTML page is required if (! empty($_REQUEST['logtohtml']) && (! empty($conf->global->MAIN_ENABLE_LOG_TO_HTML) || ! empty($conf->global->MAIN_LOGTOHTML))) // MAIN_LOGTOHTML kept for backward compatibility { @@ -1042,7 +1047,7 @@ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename=' } //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output - // If enable html log tag enabled and url parameter log defined, we show output log on HTML comments + // If html log tag enabled and url parameter log defined, we show output log on HTML comments if (! empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && ! empty($_GET["log"])) { print "\n\n'."\n"; @@ -448,7 +469,7 @@ if (! empty($conf->use_javascript_ajax)) - +
diff --git a/htdocs/core/tpl/commonfields_add.tpl.php b/htdocs/core/tpl/commonfields_add.tpl.php index 4c81e50c65c..23a60d7f675 100644 --- a/htdocs/core/tpl/commonfields_add.tpl.php +++ b/htdocs/core/tpl/commonfields_add.tpl.php @@ -19,6 +19,7 @@ * $action * $conf * $langs + * $form */ // Protection to avoid direct call of template @@ -39,7 +40,7 @@ foreach($object->fields as $key => $val) // Discard if extrafield is a hidden field on form if (abs($val['visible']) != 1) continue; - if (array_key_exists('enabled', $val) && isset($val['enabled']) && ! $val['enabled']) continue; // We don't want this field + if (array_key_exists('enabled', $val) && isset($val['enabled']) && ! verifCond($val['enabled'])) continue; // We don't want this field print '
'; if (in_array($val['type'], array('int', 'integer'))) $value = GETPOST($key, 'int'); @@ -60,4 +64,4 @@ foreach($object->fields as $key => $val) } ?> - \ No newline at end of file + diff --git a/htdocs/core/tpl/commonfields_edit.tpl.php b/htdocs/core/tpl/commonfields_edit.tpl.php index 427e9f6b894..ce41cff24d0 100644 --- a/htdocs/core/tpl/commonfields_edit.tpl.php +++ b/htdocs/core/tpl/commonfields_edit.tpl.php @@ -40,7 +40,7 @@ foreach($object->fields as $key => $val) // Discard if extrafield is a hidden field on form if (abs($val['visible']) != 1) continue; - if (array_key_exists('enabled', $val) && isset($val['enabled']) && ! $val['enabled']) continue; // We don't want this field + if (array_key_exists('enabled', $val) && isset($val['enabled']) && ! verifCond($val['enabled'])) continue; // We don't want this field print '
diff --git a/htdocs/core/tpl/passwordforgotten.tpl.php b/htdocs/core/tpl/passwordforgotten.tpl.php index 52f48c67950..4d5e1ad948b 100644 --- a/htdocs/core/tpl/passwordforgotten.tpl.php +++ b/htdocs/core/tpl/passwordforgotten.tpl.php @@ -101,7 +101,7 @@ if ($disablenofollow) echo '';
-
+
@@ -135,7 +135,7 @@ if (! empty($morelogincontent)) { ?>
-
+
diff --git a/htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php b/htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php index d791b7f1499..9031cda06f0 100644 --- a/htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php +++ b/htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php @@ -818,7 +818,7 @@ class InterfaceActionsAuto extends DolibarrTriggers $actioncomm->type_code = $object->actiontypecode; // Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...) $actioncomm->code = 'AC_'.$action; $actioncomm->label = $object->actionmsg2; - $actioncomm->note = $object->actionmsg; // TODO Replace with $actioncomm->email_msgid ? $object->email_content : $object->actionmsg + $actioncomm->note = $object->actionmsg; // TODO Replace with ($actioncomm->email_msgid ? $object->email_content : $object->actionmsg) $actioncomm->fk_project = $projectid; $actioncomm->datep = $now; $actioncomm->datef = $now; @@ -831,19 +831,22 @@ class InterfaceActionsAuto extends DolibarrTriggers $actioncomm->contactid = $contactforaction->id; $actioncomm->authorid = $user->id; // User saving action $actioncomm->userownerid = $user->id; // Owner of action - // Fields when action is en email (content should be added into note) - $actioncomm->email_msgid = $object->email_msgid; - $actioncomm->email_from = $object->email_from; - $actioncomm->email_sender= $object->email_sender; - $actioncomm->email_to = $object->email_to; - $actioncomm->email_tocc = $object->email_tocc; - $actioncomm->email_tobcc = $object->email_tobcc; + // Fields defined when action is an email (content should be into object->actionmsg to be added into note, subject into object->actionms2 to be added into label) + $actioncomm->email_msgid = $object->email_msgid; + $actioncomm->email_from = $object->email_from; + $actioncomm->email_sender = $object->email_sender; + $actioncomm->email_to = $object->email_to; + $actioncomm->email_tocc = $object->email_tocc; + $actioncomm->email_tobcc = $object->email_tobcc; $actioncomm->email_subject = $object->email_subject; - $actioncomm->errors_to = $object->errors_to; + $actioncomm->errors_to = $object->errors_to; $actioncomm->fk_element = $elementid; $actioncomm->elementtype = $elementtype; + if (property_exists($object,'attachedfiles') && is_array($object->attachedfiles) && count($object->attachedfiles)>0) { + $actioncomm->attachedfiles=$object->attachedfiles; + } if (property_exists($object,'sendtouserid') && is_array($object->sendtouserid) && count($object->sendtouserid)>0) { $actioncomm->userassigned=$object->sendtouserid; } diff --git a/htdocs/cron/list.php b/htdocs/cron/list.php index 1f810132223..f594eb2b383 100644 --- a/htdocs/cron/list.php +++ b/htdocs/cron/list.php @@ -62,15 +62,16 @@ $securitykey = GETPOST('securitykey','alpha'); $diroutputmassaction=$conf->cronjob->dir_output . '/temp/massgeneration/'.$user->id; +$object = new Cronjob($db); + // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('cronjoblist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('cronjob'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); -$object = new Cronjob($db); /* diff --git a/htdocs/datapolicy/ChangeLog.md b/htdocs/datapolicy/ChangeLog.md deleted file mode 100644 index 1c149a77e39..00000000000 --- a/htdocs/datapolicy/ChangeLog.md +++ /dev/null @@ -1,24 +0,0 @@ -**2.2** -* Fix link to accept or refuse - -**2.1** -* Change IT translations - -**2.0** -* Add date of agreement -* Add possibility to send e-mail -* Save the agreement by e-mail - -**1.2** -* Bug fixed - -**1.1** -* Add some translations -* Add some type of company -* Bug fixed - - -**1.0** -* The end of the beginning - - diff --git a/htdocs/datapolicy/README.md b/htdocs/datapolicy/README.md new file mode 100644 index 00000000000..e2bde95a6d8 --- /dev/null +++ b/htdocs/datapolicy/README.md @@ -0,0 +1,5 @@ +DataPolicy +========== + +This module provides features to be compliant with data privacy rules of your country. +A schedlued job is installed to automatically clean old record in your database. You defined what to delete and when in the setup of module. \ No newline at end of file diff --git a/htdocs/datapolicy/class/datapolicy.class.php b/htdocs/datapolicy/class/datapolicy.class.php index 4914d248490..35b618cbaca 100644 --- a/htdocs/datapolicy/class/datapolicy.class.php +++ b/htdocs/datapolicy/class/datapolicy.class.php @@ -28,7 +28,7 @@ include_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php'; /** * Class DataPolicy */ -Class DataPolicy extends Contact +Class DataPolicy { /** * getAllContactNotInformed diff --git a/htdocs/emailcollector/README.md b/htdocs/emailcollector/README.md new file mode 100644 index 00000000000..4c307183f19 --- /dev/null +++ b/htdocs/emailcollector/README.md @@ -0,0 +1,6 @@ +EMailCollector +============== + +This module provides a sheduled job that scan regularly one or several IMAP email boxes, with filtering rules, to automatically record data in your application, like +* recording the email in the history of events (event is automatically linked to its related objects if possible, for example when a customer reply to an email sent from the application, the answer is automatically linked to the good objects) +* and/or creating a lead diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php new file mode 100644 index 00000000000..e4c0d4f588f --- /dev/null +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -0,0 +1,844 @@ + + * + * 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 . + */ + +/** + * \file emailcollector/class/emailcollector.class.php + * \ingroup emailcollector + * \brief This file is a CRUD class file for EmailCollector (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; + +/** + * Class for EmailCollector + */ +class EmailCollector extends CommonObject +{ + /** + * @var string ID to identify managed object + */ + public $element = 'emailcollector'; + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element = 'emailcollector_emailcollector'; + /** + * @var int Does emailcollector support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + */ + public $ismultientitymanaged = 0; + /** + * @var int Does emailcollector support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + /** + * @var string String with name of icon for emailcollector. Must be the part after the 'object_' into object_emailcollector.png + */ + public $picto = 'generic'; + + + /** + * 'type' if the field format. + * 'label' the translation key. + * 'enabled' is a condition when the field must be managed. + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only. Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'default' is a default value for creation (can still be replaced by the global setup of default values) + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'position' is the sort order of field. + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8). + * 'css' is the CSS style to use on field. For example: 'maxwidth200' + * 'help' is a string visible as a tooltip on field + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel") + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID','visible'=>2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1), + 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'default'=>1, 'notnull'=>1, 'index'=>1, 'position'=>20), + 'ref' =>array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>10, 'searchall'=>1, 'help'=>'Example: MyCollector1'), + 'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'visible'=>1, 'enabled'=>1, 'position'=>30, 'notnull'=>-1, 'searchall'=>1, 'help'=>'Example: My Email collector'), + 'description' => array('type'=>'text', 'label'=>'Description', 'visible'=>-1, 'enabled'=>1, 'position'=>60, 'notnull'=>-1), + 'host' => array('type'=>'varchar(255)', 'label'=>'EMailHost', 'visible'=>1, 'enabled'=>1, 'position'=>100, 'notnull'=>1, 'searchall'=>1, 'comment'=>"IMAP server", 'help'=>'Example: imap.gmail.com'), + 'user' => array('type'=>'varchar(128)', 'label'=>'Login', 'visible'=>1, 'enabled'=>1, 'position'=>101, 'notnull'=>1, 'index'=>1, 'comment'=>"IMAP login", 'help'=>'Example: myacount@gmail.com'), + 'password' => array('type'=>'password', 'label'=>'Password', 'visible'=>-1, 'enabled'=>1, 'position'=>102, 'notnull'=>1, 'comment'=>"IMAP password"), + 'source_directory' => array('type'=>'varchar(255)', 'label'=>'MailboxSourceDirectory', 'visible'=>-1, 'enabled'=>1, 'position'=>103, 'notnull'=>1, 'default' => 'Inbox'), + //'filter' => array('type'=>'text', 'label'=>'Filter', 'visible'=>1, 'enabled'=>1, 'position'=>105), + //'actiontodo' => array('type'=>'varchar(255)', 'label'=>'ActionToDo', 'visible'=>1, 'enabled'=>1, 'position'=>106), + 'target_directory' => array('type'=>'varchar(255)', 'label'=>'MailboxTargetDirectory', 'visible'=>1, 'enabled'=>1, 'position'=>110, 'notnull'=>0, 'comment'=>"Where to store messages once processed"), + 'datelastresult' => array('type'=>'datetime', 'label'=>'DateLastResult', 'visible'=>1, 'enabled'=>'$action != "create" && $action != "edit"', 'position'=>121, 'notnull'=>-1,), + 'lastresult' => array('type'=>'varchar(255)', 'label'=>'LastResult', 'visible'=>1, 'enabled'=>'$action != "create" && $action != "edit"', 'position'=>122, 'notnull'=>-1,), + 'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'visible'=>0, 'enabled'=>1, 'position'=>61, 'notnull'=>-1,), + 'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'visible'=>0, 'enabled'=>1, 'position'=>62, 'notnull'=>-1,), + 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>-2, 'enabled'=>1, 'position'=>500, 'notnull'=>1,), + 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-2, 'enabled'=>1, 'position'=>501, 'notnull'=>1,), + //'date_validation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>502), + 'fk_user_creat' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'visible'=>-2, 'enabled'=>1, 'position'=>510, 'notnull'=>1,), + 'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'visible'=>-2, 'enabled'=>1, 'position'=>511, 'notnull'=>-1,), + //'fk_user_valid' =>array('type'=>'integer', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>512), + 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'visible'=>-2, 'enabled'=>1, 'position'=>1000, 'notnull'=>-1,), + 'status' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>1000, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array('0'=>'Inactive', '1'=>'Active')) + ); + + + /** + * @var int ID + */ + public $rowid; + + /** + * @var string Ref + */ + public $ref; + + /** + * @var int Entity + */ + public $entity; + + /** + * @var string label + */ + public $label; + + + /** + * @var int Status + */ + public $status; + + public $date_creation; + public $tms; + + /** + * @var int ID + */ + public $fk_user_creat; + + /** + * @var int ID + */ + public $fk_user_modif; + + public $import_key; + + + public $host; + public $user; + public $password; + public $source_directory; + public $target_directory; + public $datelastresult; + public $lastresult; + // END MODULEBUILDER PROPERTIES + + public $filters; + public $actions; + + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $conf, $langs, $user; + + $this->db = $db; + + if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) $this->fields['rowid']['visible']=0; + if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) $this->fields['entity']['enabled']=0; + + // Unset fields that are disabled + foreach($this->fields as $key => $val) + { + if (isset($val['enabled']) && empty($val['enabled'])) + { + unset($this->fields[$key]); + } + } + + // Translate some data of arrayofkeyval + foreach($this->fields as $key => $val) + { + if (is_array($this->fields['status']['arrayofkeyval'])) + { + foreach($this->fields['status']['arrayofkeyval'] as $key2 => $val2) + { + $this->fields['status']['arrayofkeyval'][$key2]=$langs->trans($val2); + } + } + } + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function create(User $user, $notrigger = false) + { + return $this->createCommon($user, $notrigger); + } + + /** + * Clone and object into another one + * + * @param User $user User that creates + * @param int $fromid Id of object to clone + * @return mixed New object created, <0 if KO + */ + public function createFromClone(User $user, $fromid) + { + global $langs, $hookmanager, $extrafields; + $error = 0; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $object = new self($this->db); + + $this->db->begin(); + + // Load source object + $object->fetchCommon($fromid); + // Reset some properties + unset($object->id); + unset($object->fk_user_creat); + unset($object->import_key); + + // Clear fields + $object->ref = "copy_of_".$object->ref; + $object->title = $langs->trans("CopyOf")." ".$object->title; + // ... + // Clear extrafields that are unique + if (is_array($object->array_options) && count($object->array_options) > 0) + { + $extrafields->fetch_name_optionals_label($this->element); + foreach($object->array_options as $key => $option) + { + $shortkey = preg_replace('/options_/', '', $key); + if (! empty($extrafields->attributes[$this->element]['unique'][$shortkey])) + { + //var_dump($key); var_dump($clonedObj->array_options[$key]); exit; + unset($object->array_options[$key]); + } + } + } + + // Create clone + $object->context['createfromclone'] = 'createfromclone'; + $result = $object->createCommon($user); + if ($result < 0) { + $error++; + $this->error = $object->error; + $this->errors = $object->errors; + } + + // End + if (!$error) { + $this->db->commit(); + return $object; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @param string $ref Ref + * @return int <0 if KO, 0 if not found, >0 if OK + */ + public function fetch($id, $ref = null) + { + $result = $this->fetchCommon($id, $ref); + //if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines(); + return $result; + } + + /** + * Load object lines in memory from the database + * + * @return int <0 if KO, 0 if not found, >0 if OK + */ + /*public function fetchLines() + { + $this->lines=array(); + + // Load lines with object EmailCollectorLine + + return count($this->lines)?1:0; + }*/ + + /** + * Fetch all account and load objects into an array + * + * @param User $user User + * @param int $activeOnly filter if active + * @param string $sortfield field for sorting + * @param string $sortorder sorting order + * @param int $limit sort limit + * @param int $page page to start on + * @return array Array with key => EmailCollector object + */ + public function fetchAll(User $user, $activeOnly = 0, $sortfield = 's.rowid', $sortorder = 'ASC', $limit = 100, $page = 0) + { + global $langs; + + $obj_ret = array(); + + $socid = $user->societe_id ? $user->societe_id : ''; + + // If the internal user must only see his customers, force searching by him + if (! $user->rights->societe->client->voir && !$socid) { + $search_sale = $user->id; + } + $sql = "SELECT s.rowid"; + //if ((!$user->rights->societe->client->voir && !$socid) || $search_sale > 0) { + // $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects) + //} + $sql.= " FROM ".MAIN_DB_PREFIX."emailcollector as s"; + + //if ((!$user->rights->societe->client->voir && !$socid) || $search_sale > 0) { + // $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale + //} + //$sql.= ", ".MAIN_DB_PREFIX."c_stcomm as st"; + //$sql.= " WHERE s.fk_stcomm = st.id"; + + // Example of use $mode + //if ($mode == 1) $sql.= " AND s.client IN (1, 3)"; + //if ($mode == 2) $sql.= " AND s.client IN (2, 3)"; + + $sql.= ' WHERE s.entity IN ('.getEntity('emailcollector').')'; + //if ((!$user->rights->societe->client->voir && !$socid) || $search_sale > 0) { + // $sql.= " AND s.fk_soc = sc.fk_soc"; + //} + //if ($socid) { + // $sql.= " AND s.fk_soc = ".$socid; + //} + if ($activeOnly) { + $sql.= " AND s.status = 1"; + } + $sql.= $this->db->order($sortfield, $sortorder); + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql.= $this->db->plimit($limit + 1, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + while ($i < $num) { + $obj = $this->db->fetch_object($result); + $emailcollector_static = new EmailCollector($this->db); + if ($emailcollector_static->fetch($obj->rowid)) { + $obj_ret[] = $emailcollector_static; + } + $i++; + } + } else { + dol_syslog(__METHOD__.':: Error when retrieve emailcollector list', LOG_ERR); + $ret = -1; + } + if (! count($obj_ret)) { + dol_syslog(__METHOD__.':: No emailcollector found', LOG_DEBUG); + } + + return $obj_ret; + } + + /** + * Update object into database + * + * @param User $user User that modifies + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function update(User $user, $notrigger = false) + { + return $this->updateCommon($user, $notrigger); + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function delete(User $user, $notrigger = false) + { + return $this->deleteCommon($user, $notrigger); + } + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) + * @param string $option On what the link point to ('nolink', ...) + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string String with URL + */ + function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1) + { + global $db, $conf, $langs, $hookmanager; + global $dolibarr_main_authentication, $dolibarr_main_demo; + global $menumanager; + + if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips + + $result = ''; + $companylink = ''; + + $label = '' . $langs->trans("EmailCollector") . ''; + $label.= '
'; + $label.= '' . $langs->trans('Ref') . ': ' . $this->ref; + + $url = dol_buildpath('/admin/emailcollector_card.php', 1).'?id='.$this->id; + + if ($option != 'nolink') + { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { + $add_save_lastsearch_values=1; + } + if ($add_save_lastsearch_values) { + $url.='&save_lastsearch_values=1'; + } + } + + $linkclose=''; + if (empty($notooltip)) + { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowEmailCollector"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; + + /* + $hookmanager->initHooks(array('myobjectdao')); + $parameters=array('id'=>$this->id); + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $linkclose = $hookmanager->resPrint; + */ + } + else $linkclose = ($morecss?' class="'.$morecss.'"':''); + + $linkstart = ''; + $linkend=''; + + $result .= $linkstart; + if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); + if ($withpicto != 2) $result.= $this->ref; + $result .= $linkend; + //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + global $action,$hookmanager; + $hookmanager->initHooks(array('emailcollectordao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + + return $result; + } + + /** + * Return label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLibStatut($mode=0) + { + return $this->LibStatut($this->status, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return the status + * + * @param int $status Id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function LibStatut($status, $mode=0) + { + // phpcs:enable + if (empty($this->labelstatus)) + { + global $langs; + //$langs->load("mymodule"); + $this->labelstatus[1] = $langs->trans('Enabled'); + $this->labelstatus[0] = $langs->trans('Disabled'); + } + + if ($mode == 0) + { + return $this->labelstatus[$status]; + } + elseif ($mode == 1) + { + return $this->labelstatus[$status]; + } + elseif ($mode == 2) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + } + elseif ($mode == 3) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + elseif ($mode == 4) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + } + elseif ($mode == 5) + { + if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + elseif ($mode == 6) + { + if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + } + + /** + * Charge les informations d'ordre info dans l'objet commande + * + * @param int $id Id of order + * @return void + */ + public function info($id) + { + $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; + $sql.= ' fk_user_creat, fk_user_modif'; + $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; + $sql.= ' WHERE t.rowid = '.$id; + $result=$this->db->query($sql); + if ($result) + { + if ($this->db->num_rows($result)) + { + $obj = $this->db->fetch_object($result); + $this->id = $obj->rowid; + if ($obj->fk_user_author) + { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } + + if ($obj->fk_user_valid) + { + $vuser = new User($this->db); + $vuser->fetch($obj->fk_user_valid); + $this->user_validation = $vuser; + } + + if ($obj->fk_user_cloture) + { + $cluser = new User($this->db); + $cluser->fetch($obj->fk_user_cloture); + $this->user_cloture = $cluser; + } + + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = $this->db->jdate($obj->datem); + $this->date_validation = $this->db->jdate($obj->datev); + } + + $this->db->free($result); + } + else + { + dol_print_error($this->db); + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + $this->initAsSpecimenCommon(); + } + + /** + * Fetch filters + * + * @return int <0 if KO, >0 if OK + */ + public function fetchFilters() + { + $this->filters = array(); + + $sql='SELECT rowid, type, rulevalue, status FROM '.MAIN_DB_PREFIX.'emailcollector_emailcollectorfilter WHERE fk_emailcollector = '.$this->id; + + $resql = $this->db->query($sql); + if ($resql) + { + $num=$this->db->num_rows($resql); + $i = 0; + while($i < $num) + { + $obj=$this->db->fetch_object($resql); + $this->filters[$obj->rowid]=array('id'=>$obj->rowid, 'type'=>$obj->type, 'rulevalue'=>$obj->rulevalue, 'status'=>$obj->status); + $i++; + } + $this->db->free($resql); + } + else dol_print_error($this->db); + + return 1; + } + + /** + * Fetch actions + * + * @return int <0 if KO, >0 if OK + */ + public function fetchActions() + { + $this->actions = array(); + + $sql='SELECT rowid, type, actionparam, status FROM '.MAIN_DB_PREFIX.'emailcollector_emailcollectoraction WHERE fk_emailcollector = '.$this->id; + + $resql = $this->db->query($sql); + if ($resql) + { + $num=$this->db->num_rows($resql); + $i = 0; + while($i < $num) + { + $obj=$this->db->fetch_object($resql); + $this->actions[$obj->rowid]=array('id'=>$obj->rowid, 'type'=>$obj->type, 'actionparam'=>$obj->actionparam, 'status'=>$obj->status); + $i++; + } + $this->db->free($resql); + } + else dol_print_error($this->db); + } + + + /** + * Action executed by scheduler + * CAN BE A CRON TASK. In such a case, paramerts come from the schedule job setup field 'Parameters' + * + * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) + */ + //public function doScheduledJob($param1, $param2, ...) + public function doCollect() + { + global $conf, $langs, $user; + + //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log'; + + dol_syslog("EmailCollector::doCollect start", LOG_DEBUG); + + $error = 0; + $this->output = ''; + $this->error=''; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $now = dol_now(); + + if (empty($this->host)) + { + $this->error=$langs->trans('ErrorFieldRequired', 'EMailHost'); + return -1; + } + if (empty($this->user)) + { + $this->error=$langs->trans('ErrorFieldRequired', 'Login'); + return -1; + } + if (empty($this->source_directory)) + { + $this->error=$langs->trans('ErrorFieldRequired', 'MailboxSourceDirectory'); + return -1; + } + if (! function_exists('imap_open')) + { + $this->error='IMAP function not enabled on your PHP'; + return -2; + } + + $this->fetchFilters(); + $this->fetchActions(); + + $sourcedir = $this->source_directory; + $targetdir = ($this->target_directory ? $server.$this->target_directory : ''); // Can be '[Gmail]/Trash' or 'mytag' + + // Connect to IMAP + $flags ='/service=imap'; // IMAP + $flags.='/ssl'; // '/tls' + $flags.='/novalidate-cert'; + //$flags.='/readonly'; + //$flags.='/debug'; + + $connectstringserver = '{'.$this->host.':993'.$flags.'}'; + $connectstring = $connectstringserver.imap_utf7_encode($sourcedir); + $connectstringtarget = $connectstringserver.imap_utf7_encode($targetdir); + + $connection = imap_open($connectstring, $this->user, $this->password); + if (! $connection) + { + $this->error = 'Failed to open IMAP connection '.$connectstring; + return -3; + } + + //$search='ALL'; + $search='UNDELETED'; + foreach($this->filters as $rule) + { + if (empty($rule['status'])) continue; + + if ($rule['key'] == 'to') $search=($search?' ':'').'TO "'.str_replace('"', '', $rule['rulevalue']).'"'; + if ($rule['key'] == 'bcc') $search=($search?' ':'').'BCC'; + if ($rule['key'] == 'cc') $search=($search?' ':'').'CC'; + if ($rule['key'] == 'from') $search=($search?' ':'').'FROM "'.str_replace('"', '', $rule['rulevalue']).'"'; + if ($rule['key'] == 'subject') $search=($search?' ':'').'SUBJECT "'.str_replace('"', '', $rule['rulevalue']).'"'; + if ($rule['key'] == 'body') $search=($search?' ':'').'BODY "'.str_replace('"', '', $rule['rulevalue']).'"'; + if ($rule['key'] == 'seen') $search=($search?' ':'').'SEEN'; + if ($rule['key'] == 'unseen') $search=($search?' ':'').'UNSEEN'; + } + + if (empty($targetdir)) // Use last date as filter if there is no targetdir defined. + { + $fromdate=0; + if ($this->datelastresult) $fromdate = $this->datelastresult; + if ($fromdate > 0) $search.=($search?' ':'').'SINCE '.dol_print_date($fromdate - 1,'dayhourrfc'); + } + dol_syslog("search string = ".$search); + + $nbemailprocessed=0; $nbactiondone=0; + + // Scan IMAP inbox + $arrayofemail= imap_search($connection, $search); + //var_dump($arrayofemail); + + // Loop on each email found + if (! empty($arrayofemail) && count($arrayofemail) > 0) + { + foreach($arrayofemail as $imapemail) + { + if ($nbemailprocessed > 100) break; // Do not process more than 100 email per launch + + $errorforactions = 0; + + $this->db->begin(); + + $overview = imap_fetch_overview($connection, $imapemail, 0); + $header = imap_fetchheader($connection, $imapemail, 0); + $message = imap_body($connection, $imapemail, 0); + // imap_fetchstructure($connection, $imapemail, 0); + // imap_fetchbody($connection, $imapemail, 1) may be text/plain, 2 may be text/html + + /*var_dump($overview); + var_dump($header); + var_dump($message); + */ + + // Do operationss + foreach($this->actions as $operation) + { + if ($errorforactions) break; + if (empty($operation['status'])) continue; + + // Make Operation + + + + if (! $errorforactions) + { + $nbactiondone++; + } + } + + // Move email + if (! $errorforactions && $targetdir) + { + dol_syslog("EmailCollector::doCollect move message ".$imapemail." to ".$connectstringtarget, LOG_DEBUG); + $res = imap_mail_move($connection, $imapemail, $targetdir, 0); + if ($res == false) { + $error++; + $this->errors = imap_last_error(); + dol_syslog(imap_last_error()); + } + } + + $nbemailprocessed++; + + $this->db->commit(); + } + + $this->output=$langs->trans('XEmailsDoneYActionsDone', $nbemailprocessed, $nbactiondone); + } + else + { + $this->output=$langs->trans('NoNewEmailToProcess'); + } + + imap_expunge($connection); // To validate any move + + imap_close($connection); + + $this->datelastresult = $now; + $this->lastresult = $this->output; + $this->update($user); + + dol_syslog("EmailCollector::doCollect end", LOG_DEBUG); + + return $error; + } +} diff --git a/htdocs/emailcollector/class/emailcollectoraction.class.php b/htdocs/emailcollector/class/emailcollectoraction.class.php new file mode 100644 index 00000000000..42eecd097bd --- /dev/null +++ b/htdocs/emailcollector/class/emailcollectoraction.class.php @@ -0,0 +1,513 @@ + + * Copyright (C) ---Put here your own copyright and developer email--- + * + * 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 . + */ + +/** + * \file class/emailcollectoraction.class.php + * \ingroup emailcollector + * \brief This file is a CRUD class file for EmailCollectorAction (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; + +/** + * Class for EmailCollectorAction + */ +class EmailCollectorAction extends CommonObject +{ + /** + * @var string ID to identify managed object + */ + public $element = 'emailcollectoraction'; + + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element = 'emailcollector_emailcollectoraction'; + + /** + * @var int Does emailcollectoraction support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + */ + public $ismultientitymanaged = 0; + + /** + * @var int Does emailcollectoraction support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + + /** + * @var string String with name of icon for emailcollectoraction. Must be the part after the 'object_' into object_emailcollectoraction.png + */ + public $picto = 'emailcollectoraction@emailcollector'; + + + /** + * 'type' if the field format. + * 'label' the translation key. + * 'enabled' is a condition when the field must be managed. + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only. Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'default' is a default value for creation (can still be replaced by the global setup of default values) + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'position' is the sort order of field. + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8). + * 'css' is the CSS style to use on field. For example: 'maxwidth200' + * 'help' is a string visible as a tooltip on field + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel") + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id",), + 'fk_emailcollector' => array('type'=>'integer', 'label'=>'Id of emailcollector', 'foreignkey'=>'emailcollecotr.rowid'), + 'type' => array('type'=>'varchar(128)', 'label'=>'Type', 'enabled'=>1, 'visible'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1), + 'actionparam' => array('type'=>'varchar(255)', 'label'=>'ParamForAction', 'enabled'=>1, 'visible'=>1, 'position'=>40, 'notnull'=>-1), + 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>500, 'notnull'=>1,), + 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'position'=>501, 'notnull'=>1,), + 'fk_user_creat' => array('type'=>'integer', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-2, 'position'=>510, 'notnull'=>1, 'foreignkey'=>'llx_user.rowid',), + 'fk_user_modif' => array('type'=>'integer', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'position'=>511, 'notnull'=>-1,), + 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>1000, 'notnull'=>-1,), + 'status' => array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'position'=>1000, 'notnull'=>1, 'default'=>1, 'arrayofkeyval'=>array('0'=>'Disabled', '1'=>'Enabled')), + ); + public $rowid; + public $fk_emailcollector; + public $type; + public $actionparam; + public $date_creation; + public $tms; + public $fk_user_creat; + public $fk_user_modif; + public $import_key; + public $status; + // END MODULEBUILDER PROPERTIES + + + + // If this object has a subtable with lines + + /** + * @var int Name of subtable line + */ + //public $table_element_line = 'emailcollectoractiondet'; + + /** + * @var int Field with ID of parent key if this field has a parent + */ + //public $fk_element = 'fk_emailcollectoraction'; + + /** + * @var int Name of subtable class that manage subtable lines + */ + //public $class_element_line = 'EmailcollectorActionline'; + + /** + * @var array Array of child tables (child tables to delete before deleting a record) + */ + //protected $childtables=array('emailcollectoractiondet'); + + /** + * @var EmailcollectorActionLine[] Array of subtable lines + */ + //public $lines = array(); + + + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $conf, $langs, $user; + + $this->db = $db; + + if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) $this->fields['rowid']['visible']=0; + if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) $this->fields['entity']['enabled']=0; + + // Unset fields that are disabled + foreach($this->fields as $key => $val) + { + if (isset($val['enabled']) && empty($val['enabled'])) + { + unset($this->fields[$key]); + } + } + + // Translate some data of arrayofkeyval + foreach($this->fields as $key => $val) + { + if (is_array($this->fields['status']['arrayofkeyval'])) + { + foreach($this->fields['status']['arrayofkeyval'] as $key2 => $val2) + { + $this->fields['status']['arrayofkeyval'][$key2]=$langs->trans($val2); + } + } + } + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function create(User $user, $notrigger = false) + { + global $langs; + if (empty($this->type)) + { + $langs->load("errors"); + $this->errors[]=$langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")); + return -1; + } + + return $this->createCommon($user, $notrigger); + } + + /** + * Clone and object into another one + * + * @param User $user User that creates + * @param int $fromid Id of object to clone + * @return mixed New object created, <0 if KO + */ + public function createFromClone(User $user, $fromid) + { + global $langs, $hookmanager, $extrafields; + $error = 0; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $object = new self($this->db); + + $this->db->begin(); + + // Load source object + $object->fetchCommon($fromid); + // Reset some properties + unset($object->id); + unset($object->fk_user_creat); + unset($object->import_key); + + // Clear fields + $object->ref = "copy_of_".$object->ref; + $object->title = $langs->trans("CopyOf")." ".$object->title; + // ... + // Clear extrafields that are unique + if (is_array($object->array_options) && count($object->array_options) > 0) + { + $extrafields->fetch_name_optionals_label($this->element); + foreach($object->array_options as $key => $option) + { + $shortkey = preg_replace('/options_/', '', $key); + if (! empty($extrafields->attributes[$this->element]['unique'][$shortkey])) + { + //var_dump($key); var_dump($clonedObj->array_options[$key]); exit; + unset($object->array_options[$key]); + } + } + } + + // Create clone + $object->context['createfromclone'] = 'createfromclone'; + $result = $object->createCommon($user); + if ($result < 0) { + $error++; + $this->error = $object->error; + $this->errors = $object->errors; + } + + // End + if (!$error) { + $this->db->commit(); + return $object; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @param string $ref Ref + * @return int <0 if KO, 0 if not found, >0 if OK + */ + public function fetch($id, $ref = null) + { + $result = $this->fetchCommon($id, $ref); + if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines(); + return $result; + } + + /** + * Load object lines in memory from the database + * + * @return int <0 if KO, 0 if not found, >0 if OK + */ + /*public function fetchLines() + { + $this->lines=array(); + + // Load lines with object EmailcollectorActionLine + + return count($this->lines)?1:0; + }*/ + + /** + * Update object into database + * + * @param User $user User that modifies + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function update(User $user, $notrigger = false) + { + return $this->updateCommon($user, $notrigger); + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function delete(User $user, $notrigger = false) + { + return $this->deleteCommon($user, $notrigger); + } + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) + * @param string $option On what the link point to ('nolink', ...) + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string String with URL + */ + function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1) + { + global $db, $conf, $langs, $hookmanager; + global $dolibarr_main_authentication, $dolibarr_main_demo; + global $menumanager; + + if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips + + $result = ''; + $companylink = ''; + + $label = '' . $langs->trans("EmailcollectorAction") . ''; + $label.= '
'; + $label.= '' . $langs->trans('Ref') . ': ' . $this->ref; + + $url = dol_buildpath('/emailcollector/emailcollectoraction_card.php',1).'?id='.$this->id; + + if ($option != 'nolink') + { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; + if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + } + + $linkclose=''; + if (empty($notooltip)) + { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowEmailcollectorAction"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; + + /* + $hookmanager->initHooks(array('emailcollectoractiondao')); + $parameters=array('id'=>$this->id); + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $linkclose = $hookmanager->resPrint; + */ + } + else $linkclose = ($morecss?' class="'.$morecss.'"':''); + + $linkstart = ''; + $linkend=''; + + $result .= $linkstart; + if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); + if ($withpicto != 2) $result.= $this->ref; + $result .= $linkend; + //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + global $action,$hookmanager; + $hookmanager->initHooks(array('emailcollectoractiondao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + + return $result; + } + + /** + * Return label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLibStatut($mode=0) + { + return $this->LibStatut($this->status, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return the status + * + * @param int $status Id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function LibStatut($status, $mode=0) + { + // phpcs:enable + if (empty($this->labelstatus)) + { + global $langs; + //$langs->load("emailcollector"); + $this->labelstatus[1] = $langs->trans('Enabled'); + $this->labelstatus[0] = $langs->trans('Disabled'); + } + + if ($mode == 0) + { + return $this->labelstatus[$status]; + } + elseif ($mode == 1) + { + return $this->labelstatus[$status]; + } + elseif ($mode == 2) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + } + elseif ($mode == 3) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + elseif ($mode == 4) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + } + elseif ($mode == 5) + { + if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + elseif ($mode == 6) + { + if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + } + + /** + * Charge les informations d'ordre info dans l'objet commande + * + * @param int $id Id of order + * @return void + */ + public function info($id) + { + $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; + $sql.= ' fk_user_creat, fk_user_modif'; + $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; + $sql.= ' WHERE t.rowid = '.$id; + $result=$this->db->query($sql); + if ($result) + { + if ($this->db->num_rows($result)) + { + $obj = $this->db->fetch_object($result); + $this->id = $obj->rowid; + if ($obj->fk_user_author) + { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } + + if ($obj->fk_user_valid) + { + $vuser = new User($this->db); + $vuser->fetch($obj->fk_user_valid); + $this->user_validation = $vuser; + } + + if ($obj->fk_user_cloture) + { + $cluser = new User($this->db); + $cluser->fetch($obj->fk_user_cloture); + $this->user_cloture = $cluser; + } + + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = $this->db->jdate($obj->datem); + $this->date_validation = $this->db->jdate($obj->datev); + } + + $this->db->free($result); + } + else + { + dol_print_error($this->db); + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + $this->initAsSpecimenCommon(); + } +} diff --git a/htdocs/emailcollector/class/emailcollectorfilter.class.php b/htdocs/emailcollector/class/emailcollectorfilter.class.php new file mode 100644 index 00000000000..23c8f7a02b2 --- /dev/null +++ b/htdocs/emailcollector/class/emailcollectorfilter.class.php @@ -0,0 +1,491 @@ + + * Copyright (C) ---Put here your own copyright and developer email--- + * + * 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 . + */ + +/** + * \file emailcollector/class/emailcollectorfilter.class.php + * \ingroup emailcollector + * \brief This file is a CRUD class file for EmailCollectorFilter (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; + +/** + * Class for EmailCollectorFilter + */ +class EmailCollectorFilter extends CommonObject +{ + /** + * @var string ID to identify managed object + */ + public $element = 'emailcollectorfilter'; + + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element = 'emailcollector_emailcollectorfilter'; + + /** + * @var int Does emailcollectorfilter support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + */ + public $ismultientitymanaged = 0; + + /** + * @var int Does emailcollectorfilter support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + + /** + * @var string String with name of icon for emailcollectorfilter. Must be the part after the 'object_' into object_emailcollectorfilter.png + */ + public $picto = 'emailcollectorfilter@emailcollector'; + + + /** + * 'type' if the field format. + * 'label' the translation key. + * 'enabled' is a condition when the field must be managed. + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only. Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'default' is a default value for creation (can still be replaced by the global setup of default values) + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'position' is the sort order of field. + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8). + * 'css' is the CSS style to use on field. For example: 'maxwidth200' + * 'help' is a string visible as a tooltip on field + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel") + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id",), + 'fk_emailcollector' => array('type'=>'integer', 'label'=>'Id of emailcollector', 'foreignkey'=>'emailcollector.rowid'), + 'type' => array('type'=>'varchar(128)', 'label'=>'Type', 'enabled'=>1, 'visible'=>1, 'position'=>10, 'notnull'=>1,), + 'rulevalue' => array('type'=>'varchar(255)', 'label'=>'ValueOfRule', 'enabled'=>1, 'visible'=>1, 'position'=>30, 'notnull'=>-1, 'help'=>"Value of Rule",), + 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>500, 'notnull'=>1,), + 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'position'=>501, 'notnull'=>1,), + 'fk_user_creat' => array('type'=>'integer', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-2, 'position'=>510, 'notnull'=>1, 'foreignkey'=>'llx_user.rowid',), + 'fk_user_modif' => array('type'=>'integer', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'position'=>511, 'notnull'=>-1,), + 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>1000, 'notnull'=>-1,), + 'status' => array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'position'=>1000, 'notnull'=>1, 'default'=>1, 'arrayofkeyval'=>array('0'=>'Disabled', '1'=>'Enabled')), + ); + public $rowid; + public $fk_emailcollector; + public $type; + public $rulevalue; + public $date_creation; + public $tms; + public $fk_user_creat; + public $fk_user_modif; + public $import_key; + public $status; + // END MODULEBUILDER PROPERTIES + + + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $conf, $langs, $user; + + $this->db = $db; + + if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) $this->fields['rowid']['visible']=0; + if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) $this->fields['entity']['enabled']=0; + + // Unset fields that are disabled + foreach($this->fields as $key => $val) + { + if (isset($val['enabled']) && empty($val['enabled'])) + { + unset($this->fields[$key]); + } + } + + // Translate some data of arrayofkeyval + foreach($this->fields as $key => $val) + { + if (is_array($this->fields['status']['arrayofkeyval'])) + { + foreach($this->fields['status']['arrayofkeyval'] as $key2 => $val2) + { + $this->fields['status']['arrayofkeyval'][$key2]=$langs->trans($val2); + } + } + } + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function create(User $user, $notrigger = false) + { + global $langs; + if (empty($this->type)) + { + $langs->load("errors"); + $this->errors[]=$langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")); + return -1; + } + if (empty($this->rulevalue)) + { + $langs->load("errors"); + $this->errors[]=$langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("RuleValue")); + return -1; + } + + return $this->createCommon($user, $notrigger); + } + + /** + * Clone and object into another one + * + * @param User $user User that creates + * @param int $fromid Id of object to clone + * @return mixed New object created, <0 if KO + */ + public function createFromClone(User $user, $fromid) + { + global $langs, $hookmanager, $extrafields; + $error = 0; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $object = new self($this->db); + + $this->db->begin(); + + // Load source object + $object->fetchCommon($fromid); + // Reset some properties + unset($object->id); + unset($object->fk_user_creat); + unset($object->import_key); + + // Clear fields + $object->ref = "copy_of_".$object->ref; + $object->title = $langs->trans("CopyOf")." ".$object->title; + // ... + // Clear extrafields that are unique + if (is_array($object->array_options) && count($object->array_options) > 0) + { + $extrafields->fetch_name_optionals_label($this->element); + foreach($object->array_options as $key => $option) + { + $shortkey = preg_replace('/options_/', '', $key); + if (! empty($extrafields->attributes[$this->element]['unique'][$shortkey])) + { + //var_dump($key); var_dump($clonedObj->array_options[$key]); exit; + unset($object->array_options[$key]); + } + } + } + + // Create clone + $object->context['createfromclone'] = 'createfromclone'; + $result = $object->createCommon($user); + if ($result < 0) { + $error++; + $this->error = $object->error; + $this->errors = $object->errors; + } + + // End + if (!$error) { + $this->db->commit(); + return $object; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @param string $ref Ref + * @return int <0 if KO, 0 if not found, >0 if OK + */ + public function fetch($id, $ref = null) + { + $result = $this->fetchCommon($id, $ref); + if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines(); + return $result; + } + + /** + * Load object lines in memory from the database + * + * @return int <0 if KO, 0 if not found, >0 if OK + */ + /*public function fetchLines() + { + $this->lines=array(); + + // Load lines with object EmailcollectorFilterLine + + return count($this->lines)?1:0; + }*/ + + /** + * Update object into database + * + * @param User $user User that modifies + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function update(User $user, $notrigger = false) + { + return $this->updateCommon($user, $notrigger); + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function delete(User $user, $notrigger = false) + { + return $this->deleteCommon($user, $notrigger); + } + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) + * @param string $option On what the link point to ('nolink', ...) + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string String with URL + */ + function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1) + { + global $db, $conf, $langs, $hookmanager; + global $dolibarr_main_authentication, $dolibarr_main_demo; + global $menumanager; + + if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips + + $result = ''; + $companylink = ''; + + $label = '' . $langs->trans("EmailcollectorFilter") . ''; + $label.= '
'; + $label.= '' . $langs->trans('Ref') . ': ' . $this->ref; + + $url = dol_buildpath('/emailcollector/emailcollectorfilter_card.php',1).'?id='.$this->id; + + if ($option != 'nolink') + { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; + if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + } + + $linkclose=''; + if (empty($notooltip)) + { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowEmailcollectorFilter"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; + + /* + $hookmanager->initHooks(array('emailcollectorfilterdao')); + $parameters=array('id'=>$this->id); + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $linkclose = $hookmanager->resPrint; + */ + } + else $linkclose = ($morecss?' class="'.$morecss.'"':''); + + $linkstart = ''; + $linkend=''; + + $result .= $linkstart; + if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); + if ($withpicto != 2) $result.= $this->ref; + $result .= $linkend; + //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + global $action,$hookmanager; + $hookmanager->initHooks(array('emailcollectorfilterdao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + + return $result; + } + + /** + * Return label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLibStatut($mode=0) + { + return $this->LibStatut($this->status, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return the status + * + * @param int $status Id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function LibStatut($status, $mode=0) + { + // phpcs:enable + if (empty($this->labelstatus)) + { + global $langs; + //$langs->load("emailcollector"); + $this->labelstatus[1] = $langs->trans('Enabled'); + $this->labelstatus[0] = $langs->trans('Disabled'); + } + + if ($mode == 0) + { + return $this->labelstatus[$status]; + } + elseif ($mode == 1) + { + return $this->labelstatus[$status]; + } + elseif ($mode == 2) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + } + elseif ($mode == 3) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + elseif ($mode == 4) + { + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + } + elseif ($mode == 5) + { + if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + elseif ($mode == 6) + { + if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); + } + } + + /** + * Charge les informations d'ordre info dans l'objet commande + * + * @param int $id Id of order + * @return void + */ + public function info($id) + { + $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; + $sql.= ' fk_user_creat, fk_user_modif'; + $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; + $sql.= ' WHERE t.rowid = '.$id; + $result=$this->db->query($sql); + if ($result) + { + if ($this->db->num_rows($result)) + { + $obj = $this->db->fetch_object($result); + $this->id = $obj->rowid; + if ($obj->fk_user_author) + { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } + + if ($obj->fk_user_valid) + { + $vuser = new User($this->db); + $vuser->fetch($obj->fk_user_valid); + $this->user_validation = $vuser; + } + + if ($obj->fk_user_cloture) + { + $cluser = new User($this->db); + $cluser->fetch($obj->fk_user_cloture); + $this->user_cloture = $cluser; + } + + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = $this->db->jdate($obj->datem); + $this->date_validation = $this->db->jdate($obj->datev); + } + + $this->db->free($result); + } + else + { + dol_print_error($this->db); + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + $this->initAsSpecimenCommon(); + } +} + diff --git a/htdocs/emailcollector/lib/emailcollector.lib.php b/htdocs/emailcollector/lib/emailcollector.lib.php new file mode 100644 index 00000000000..e7cc3bcb3a0 --- /dev/null +++ b/htdocs/emailcollector/lib/emailcollector.lib.php @@ -0,0 +1,85 @@ +. + */ + +/** + * \file emailcollector/lib/emailcollector.lib.php + * \ingroup emailcollector + * \brief Library files with common functions for EmailCollector + */ + + +/** + * Prepare array of tabs for EmailCollector + * + * @param EmailCollector $object EmailCollector + * @return array Array of tabs + */ +function emailcollectorPrepareHead($object) +{ + global $db, $langs, $conf; + + $langs->load("emailcollector@emailcollector"); + + $h = 0; + $head = array(); + + $head[$h][0] = dol_buildpath("/admin/emailcollector_card.php", 1).'?id='.$object->id; + $head[$h][1] = $langs->trans("Card"); + $head[$h][2] = 'card'; + $h++; + + /*if (isset($object->fields['note_public']) || isset($object->fields['note_private'])) + { + $nbNote = 0; + if (!empty($object->note_private)) $nbNote++; + if (!empty($object->note_public)) $nbNote++; + $head[$h][0] = dol_buildpath('/emailcollector/emailcollector_note.php', 1).'?id='.$object->id; + $head[$h][1] = $langs->trans('Notes'); + if ($nbNote > 0) $head[$h][1].= ' '.$nbNote.''; + $head[$h][2] = 'note'; + $h++; + }*/ + + /*require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; + $upload_dir = $conf->emailcollector->dir_output . "/emailcollector/" . dol_sanitizeFileName($object->ref); + $nbFiles = count(dol_dir_list($upload_dir,'files',0,'','(\.meta|_preview.*\.png)$')); + $nbLinks=Link::count($db, $object->element, $object->id); + $head[$h][0] = dol_buildpath("/emailcollector/emailcollector_document.php", 1).'?id='.$object->id; + $head[$h][1] = $langs->trans('Documents'); + if (($nbFiles+$nbLinks) > 0) $head[$h][1].= ' '.($nbFiles+$nbLinks).''; + $head[$h][2] = 'document'; + $h++; + + $head[$h][0] = dol_buildpath("/emailcollector/emailcollector_agenda.php", 1).'?id='.$object->id; + $head[$h][1] = $langs->trans("Events"); + $head[$h][2] = 'agenda'; + $h++; + */ + + // Show more tabs from modules + // Entries must be declared in modules descriptor with line + //$this->tabs = array( + // 'entity:+tabname:Title:@emailcollector:/emailcollector/mypage.php?id=__ID__' + //); // to add new tab + //$this->tabs = array( + // 'entity:-tabname:Title:@emailcollector:/emailcollector/mypage.php?id=__ID__' + //); // to remove a tab + complete_head_from_modules($conf, $langs, $object, $head, $h, 'emailcollector'); + + return $head; +} diff --git a/htdocs/emailcollector/modulebuilder.txt b/htdocs/emailcollector/modulebuilder.txt new file mode 100644 index 00000000000..24ea0d6eac5 --- /dev/null +++ b/htdocs/emailcollector/modulebuilder.txt @@ -0,0 +1,3 @@ +# DO NOT DELETE THIS FILE MANUALLY +# File to flag module built using official module template. +# When this file is present into a module directory, you can edit it with the module builder tool. Use ModuleBuilder if you want to delete module. \ No newline at end of file diff --git a/htdocs/expedition/list.php b/htdocs/expedition/list.php index 19db21671d8..b3fbc35bf26 100644 --- a/htdocs/expedition/list.php +++ b/htdocs/expedition/list.php @@ -69,13 +69,15 @@ $pagenext = $page + 1; $viewstatut=GETPOST('viewstatut'); +$object = new Expedition($db); + // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('shipmentlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('expedition'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/expensereport/card.php b/htdocs/expensereport/card.php index 27085687986..4bc41f6fa47 100644 --- a/htdocs/expensereport/card.php +++ b/htdocs/expensereport/card.php @@ -1390,7 +1390,7 @@ if ($action == 'create') $defaultselectuser=$user->fk_user; // Will work only if supervisor has permission to approve so is inside include_users if (! empty($conf->global->EXPENSEREPORT_DEFAULT_VALIDATOR)) $defaultselectuser=$conf->global->EXPENSEREPORT_DEFAULT_VALIDATOR; // Can force default approver if (GETPOST('fk_user_validator', 'int') > 0) $defaultselectuser=GETPOST('fk_user_validator', 'int'); - $s=$form->select_dolusers($defaultselectuser, "fk_user_validator", 1, "", 0, $include_users); + $s=$form->select_dolusers($defaultselectuser, "fk_user_validator", 1, "", ((empty($defaultselectuser) || empty($conf->global->EXPENSEREPORT_DEFAULT_VALIDATOR_UNCHANGEABLE))?0:1), $include_users); print $form->textwithpicto($s, $langs->trans("AnyOtherInThisListCanValidate")); } print ''; diff --git a/htdocs/expensereport/list.php b/htdocs/expensereport/list.php index 1990a2e98d6..b2b8e46d668 100644 --- a/htdocs/expensereport/list.php +++ b/htdocs/expensereport/list.php @@ -90,7 +90,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('expensereport'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" diff --git a/htdocs/expensereport/stats/index.php b/htdocs/expensereport/stats/index.php index e53757be1db..c192fb83447 100644 --- a/htdocs/expensereport/stats/index.php +++ b/htdocs/expensereport/stats/index.php @@ -2,6 +2,7 @@ /* Copyright (C) 2003-2006 Rodolphe Quiedeville * Copyright (c) 2004-2012 Laurent Destailleur * Copyright (C) 2012 Marcos García + * Copyright (C) 2018 Frédéric France * * 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 @@ -282,7 +283,7 @@ foreach ($data as $val) } print '
'; -print '
'; +print '
'; print '
'; diff --git a/htdocs/fichinter/card-rec.php b/htdocs/fichinter/card-rec.php index c75ad6ab9e7..456c27a4584 100644 --- a/htdocs/fichinter/card-rec.php +++ b/htdocs/fichinter/card-rec.php @@ -152,7 +152,7 @@ if ($action == 'add') { } } } elseif ($action == 'createfrommodel') { - $newinter = new fichinter($db); + $newinter = new Fichinter($db); // on récupère les enregistrements $object->fetch($id); @@ -176,7 +176,7 @@ if ($action == 'add') { // on créer un nouvelle intervention $extrafields = new ExtraFields($db); $extralabels = $extrafields->fetch_name_optionals_label($newinter->table_element); - $array_options = $extrafields->getOptionalsFromPost($extralabels); + $array_options = $extrafields->getOptionalsFromPost($newinter->table_element); $newinter->array_options = $array_options; $newfichinterid = $newinter->create($user); @@ -866,7 +866,7 @@ if ($action == 'create') { print "\n"; -// les filtres à faire ensuite + // les filtres à faire ensuite if ($num > 0) { while ($i < min($num, $limit)) { @@ -893,8 +893,8 @@ if ($action == 'create') { } if (! empty($conf->projet->enabled)) { print ''; - if ($objp->fk_project >0) { - $projectstatic->fecth($objp->fk_projet); + if ($objp->fk_project > 0) { + $projectstatic->fetch($objp->fk_projet); print $projectstatic->getNomUrl(1); } print ''; diff --git a/htdocs/fichinter/card.php b/htdocs/fichinter/card.php index 5865159f9b8..756613d6a8f 100644 --- a/htdocs/fichinter/card.php +++ b/htdocs/fichinter/card.php @@ -7,6 +7,7 @@ * Copyright (C) 2014-2018 Ferran Marcet * Copyright (C) 2014-2018 Charlene Benke * Copyright (C) 2015-2016 Abbes Bahfir + * Copyright (C) 2018 Philippe Grand * * 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 @@ -258,7 +259,7 @@ if (empty($reshook)) // Extrafields $extrafields = new ExtraFields($db); $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $array_options = $extrafields->getOptionalsFromPost($extralabels); + $array_options = $extrafields->getOptionalsFromPost($object->table_element); $object->array_options = $array_options; @@ -406,7 +407,7 @@ if (empty($reshook)) { // Extrafields $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $array_options = $extrafields->getOptionalsFromPost($extralabels); + $array_options = $extrafields->getOptionalsFromPost($object->table_element); $object->array_options = $array_options; @@ -1646,10 +1647,12 @@ else if ($id > 0 || ! empty($ref)) else print ''; } - // create interventional model + // create intervention model if ($object->statut == Fichinter::STATUS_DRAFT && $user->rights->ficheinter->creer && (count($object->lines) > 0)) { print ''; } diff --git a/htdocs/fichinter/list.php b/htdocs/fichinter/list.php index a74b8c09a3d..c26e642a651 100644 --- a/htdocs/fichinter/list.php +++ b/htdocs/fichinter/list.php @@ -87,7 +87,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('fichinter'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/fichinter/stats/index.php b/htdocs/fichinter/stats/index.php index f4df5f3c5f8..7ebf701d3d5 100644 --- a/htdocs/fichinter/stats/index.php +++ b/htdocs/fichinter/stats/index.php @@ -30,7 +30,7 @@ $WIDTH=DolGraph::getDefaultGraphSizeForStats('width'); $HEIGHT=DolGraph::getDefaultGraphSizeForStats('height'); $mode='customer'; -if ($mode == 'customer' && ! $user->rights->ficheinter->lire) accessforbidden(); +if (! $user->rights->ficheinter->lire) accessforbidden(); $userid=GETPOST('userid','int'); $socid=GETPOST('socid','int'); @@ -50,7 +50,7 @@ $endyear=$year; $object_status=GETPOST('object_status'); // Load translation files required by the page -$langs->loadLangs(array("interventions","suppliers","companies","other")); +$langs->loadLangs(array('interventions', 'companies', 'other', 'suppliers')); /* @@ -60,11 +60,8 @@ $langs->loadLangs(array("interventions","suppliers","companies","other")); $form=new Form($db); $objectstatic=new FichInter($db); -if ($mode == 'customer') -{ - $title=$langs->trans("InterventionStatistics"); - $dir=$conf->ficheinter->dir_temp; -} +$title=$langs->trans("InterventionStatistics"); +$dir=$conf->ficheinter->dir_temp; llxHeader('', $title); @@ -77,19 +74,18 @@ if ($object_status != '' && $object_status > -1) $stats->where .= ' AND c.fk_sta // Build graphic number of object $data = $stats->getNbByMonthWithPrevYear($endyear,$startyear); -//var_dump($data); // $data = array(array('Lib',val1,val2,val3),...) if (!$user->rights->societe->client->voir || $user->societe_id) { $filenamenb = $dir.'/interventionsnbinyear-'.$user->id.'-'.$year.'.png'; - if ($mode == 'customer') $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsnbinyear-'.$user->id.'-'.$year.'.png'; + $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsnbinyear-'.$user->id.'-'.$year.'.png'; } else { $filenamenb = $dir.'/interventionsnbinyear-'.$year.'.png'; - if ($mode == 'customer') $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsnbinyear-'.$year.'.png'; + $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsnbinyear-'.$year.'.png'; } $px1 = new DolGraph(); @@ -121,20 +117,17 @@ if (! $mesg) // Build graphic amount of object $data = $stats->getAmountByMonthWithPrevYear($endyear,$startyear); -//var_dump($data); // $data = array(array('Lib',val1,val2,val3),...) if (!$user->rights->societe->client->voir || $user->societe_id) { $filenameamount = $dir.'/interventionsamountinyear-'.$user->id.'-'.$year.'.png'; - if ($mode == 'customer') $fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsamountinyear-'.$user->id.'-'.$year.'.png'; - if ($mode == 'supplier') $fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstatssupplier&file=interventionsamountinyear-'.$user->id.'-'.$year.'.png'; + $fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsamountinyear-'.$user->id.'-'.$year.'.png'; } else { $filenameamount = $dir.'/interventionsamountinyear-'.$year.'.png'; - if ($mode == 'customer') $fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsamountinyear-'.$year.'.png'; - if ($mode == 'supplier') $fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstatssupplier&file=interventionsamountinyear-'.$year.'.png'; + $fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsamountinyear-'.$year.'.png'; } $px2 = new DolGraph(); @@ -169,14 +162,12 @@ $data = $stats->getAverageByMonthWithPrevYear($endyear, $startyear); if (!$user->rights->societe->client->voir || $user->societe_id) { $filename_avg = $dir.'/interventionsaverage-'.$user->id.'-'.$year.'.png'; - if ($mode == 'customer') $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsaverage-'.$user->id.'-'.$year.'.png'; - if ($mode == 'supplier') $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstatssupplier&file=interventionsaverage-'.$user->id.'-'.$year.'.png'; + $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsaverage-'.$user->id.'-'.$year.'.png'; } else { $filename_avg = $dir.'/interventionsaverage-'.$year.'.png'; - if ($mode == 'customer') $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsaverage-'.$year.'.png'; - if ($mode == 'supplier') $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstatssupplier&file=interventionsaverage-'.$year.'.png'; + $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=interventionstats&file=interventionsaverage-'.$year.'.png'; } $px3 = new DolGraph(); @@ -219,13 +210,12 @@ if (! count($arrayyears)) $arrayyears[$nowyear]=$nowyear; $h=0; $head = array(); -$head[$h][0] = DOL_URL_ROOT . '/commande/stats/index.php?mode='.$mode; +$head[$h][0] = DOL_URL_ROOT . '/fichinter/stats/index.php'; $head[$h][1] = $langs->trans("ByMonthYear"); $head[$h][2] = 'byyear'; $h++; -if ($mode == 'customer') $type='order_stats'; -if ($mode == 'supplier') $type='supplier_order_stats'; +$type = 'fichinter_stats'; complete_head_from_modules($conf,$langs,null,$head,$h,$type); @@ -244,9 +234,8 @@ print '
'; print ''.$langs->trans("Filter").''; // Company print ''.$langs->trans("ThirdParty").''; - if ($mode == 'customer') $filter='s.client in (1,2,3)'; - if ($mode == 'supplier') $filter='s.fournisseur = 1'; - print $form->select_company($socid,'socid',$filter,1,0,0,array(),0,'','style="width: 95%"'); + $filter = 's.client in (1,2,3)'; + print $form->select_company($socid, 'socid', $filter, 1, 0, 0, array(), 0, '', 'style="width: 95%"'); print ''; // User print ''.$langs->trans("CreatedBy").''; @@ -288,7 +277,8 @@ foreach ($data as $val) { $year = $val['year']; while (! empty($year) && $oldyear > $year+1) - { // If we have empty year + { + // If we have empty year $oldyear--; print ''; diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php index c75e111b1ad..056070d580d 100644 --- a/htdocs/fourn/class/fournisseur.product.class.php +++ b/htdocs/fourn/class/fournisseur.product.class.php @@ -560,10 +560,9 @@ class ProductFournisseur extends Product $sql = "SELECT s.nom as supplier_name, s.rowid as fourn_id,"; $sql.= " pfp.rowid as product_fourn_pri_id, pfp.ref_fourn, pfp.desc_fourn, pfp.fk_product as product_fourn_id, pfp.fk_supplier_price_expression,"; - $sql.= " pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability, pfp.charges, pfp.info_bits, pfp.delivery_time_days, pfp.supplier_reputation"; - $sql.= " ,pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + $sql.= " pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability, pfp.charges, pfp.info_bits, pfp.delivery_time_days, pfp.supplier_reputation,"; + $sql.= " pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code, pfp.datec, pfp.tms"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp, ".MAIN_DB_PREFIX."societe as s"; $sql.= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")"; $sql.= " AND pfp.fk_soc = s.rowid"; $sql.= " AND s.status=1"; // only enabled company selected @@ -603,6 +602,8 @@ class ProductFournisseur extends Product $prodfourn->fourn_tva_npr = $record["info_bits"]; $prodfourn->fk_supplier_price_expression = $record["fk_supplier_price_expression"]; $prodfourn->supplier_reputation = $record["supplier_reputation"]; + $prodfourn->date_creation = $this->db->jdate($record['datec']); + $prodfourn->date_modification = $this->db->jdate($record['tms']); $prodfourn->fourn_multicurrency_price = $record["multicurrency_price"]; $prodfourn->fourn_multicurrency_unitprice = $record["multicurrency_unitprice"]; diff --git a/htdocs/fourn/commande/list.php b/htdocs/fourn/commande/list.php index a3005302fb2..d795d1cec07 100644 --- a/htdocs/fourn/commande/list.php +++ b/htdocs/fourn/commande/list.php @@ -113,7 +113,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('commande_fournisseur'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/fourn/facture/list.php b/htdocs/fourn/facture/list.php index 11b5e177bcb..07b53981faf 100644 --- a/htdocs/fourn/facture/list.php +++ b/htdocs/fourn/facture/list.php @@ -130,7 +130,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('facture_fourn'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/fourn/facture/paiement.php b/htdocs/fourn/facture/paiement.php index e3d9c4f534e..fd313ba297c 100644 --- a/htdocs/fourn/facture/paiement.php +++ b/htdocs/fourn/facture/paiement.php @@ -89,7 +89,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('paymentsupplier'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost('paymentsupplier','','search_'); $arrayfields=array(); @@ -570,25 +570,25 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie { print '!!!'; } - + // Date Max Payment if ($objp->dlr > 0 ) { print ''; print dol_print_date($db->jdate($objp->dlr), 'day'); - + if ($invoice->hasDelay()) { print img_warning($langs->trans('Late')); } - + print ''; } else { print '--'; } - + // Multicurrency if (!empty($conf->multicurrency->enabled)) { diff --git a/htdocs/holiday/class/holiday.class.php b/htdocs/holiday/class/holiday.class.php index 6405d5cf664..f5761876795 100644 --- a/htdocs/holiday/class/holiday.class.php +++ b/htdocs/holiday/class/holiday.class.php @@ -2171,8 +2171,10 @@ class Holiday extends CommonObject $this->description='SPECIMEN description'; $this->date_debut=dol_now(); $this->date_fin=dol_now()+(24*3600); + $this->date_valid=dol_now(); $this->fk_validator=1; $this->halfday=0; $this->fk_type=1; + $this->statut=Holiday::STATUS_VALIDATED; } } diff --git a/htdocs/holiday/list.php b/htdocs/holiday/list.php index f62fbc98922..a2bdcb2a5b3 100644 --- a/htdocs/holiday/list.php +++ b/htdocs/holiday/list.php @@ -78,7 +78,7 @@ $diroutputmassaction=$conf->holiday->dir_output . '/temp/massgeneration/'.$user- $hookmanager->initHooks(array('holidaylist')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('holiday'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Default sort order (if not yet defined by previous GETPOST) if (! $sortfield) $sortfield="cp.rowid"; diff --git a/htdocs/holiday/month_report.php b/htdocs/holiday/month_report.php index 53fab6a825f..152bf554ab8 100644 --- a/htdocs/holiday/month_report.php +++ b/htdocs/holiday/month_report.php @@ -1,6 +1,7 @@ * Copyright (C) 2011 François Legastelois + * Copyright (C) 2018 Frédéric France * * 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 @@ -120,7 +121,7 @@ print ''; if ($num == 0) { - print ''.$langs->trans('None').''; + print ''.$langs->trans('None').''; } else { diff --git a/htdocs/index.php b/htdocs/index.php index 1bab889d2a2..1ef48ea9bae 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -552,7 +552,8 @@ if ($showweather) $nbworkboardempty=0; if (! empty($valid_dashboardlines)) { - $boxwork.=''; + $boxwork.='
'; + foreach($valid_dashboardlines as $board) { if (empty($board->nbtodo)) $nbworkboardempty++; @@ -592,6 +593,8 @@ if (! empty($valid_dashboardlines)) $boxwork .='
'; $boxwork .='
'; $boxwork .='
'; + + $boxwork .='
'; $boxwork .=''; } else diff --git a/htdocs/install/mysql/data/llx_c_email_templates.sql b/htdocs/install/mysql/data/llx_c_email_templates.sql index b913fca13f3..3d1a1e7c178 100644 --- a/htdocs/install/mysql/data/llx_c_email_templates.sql +++ b/htdocs/install/mysql/data/llx_c_email_templates.sql @@ -23,10 +23,10 @@ INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines) VALUES (0,'banque','thirdparty','',0,null,null,'(YourSEPAMandate)',1,1,0,'__(YourSEPAMandate)__','__(Hello)__,

\n\n__(FindYourSEPAMandate)__ :
\n__MYCOMPANY_NAME__
\n__MYCOMPANY_FULLADDRESS__

\n__(Sincerely)__
\n__USER_SIGNATURE__',null); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnAutoSubscription)' ,10,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipRequestWasReceived)__','__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourMembershipRequestWasReceived)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnMemberValidation)' ,20,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasValidated)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourMembershipWasValidated)__
__INFOS__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnAutoSubscription)' ,10,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipRequestWasReceived)__','__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourMembershipRequestWasReceived)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnMemberValidation)' ,20,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasValidated)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourMembershipWasValidated)__
__INFOS__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnNewSubscription)' ,30,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourSubscriptionWasRecorded)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourSubscriptionWasRecorded)__
\n\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingReminderForExpiredSubscription)',40,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(SubscriptionReminderEmail)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfSubscriptionReminderEmail)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnCancelation)' ,50,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasCanceled)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(YourMembershipWasCanceled)__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingAnEMailToMember)' ,60,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(CardContent)__', '__(Hello)__,

\n\n__(ThisIsContentOfYourCard)__
\n__(ID)__ : __ID__
\n__(Civiliyty)__ : __MEMBER_CIVILITY__
\n__(Firstname)__ : __MEMBER_FIRSTNAME__
\n__(Lastname)__ : __MEMBER_LASTNAME__
\n__(Fullname)__ : __MEMBER_FULLNAME__
\n__(Company)__ : __MEMBER_COMPANY__
\n__(Address)__ : __MEMBER_ADDRESS__
\n__(Zip)__ : __MEMBER_ZIP__
\n__(Town)__ : __MEMBER_TOWN__
\n__(Country)__ : __MEMBER_COUNTRY__
\n__(Email)__ : __MEMBER_EMAIL__
\n__(Birthday)__ : __MEMBER_BIRTH__
\n__(Photo)__ : __MEMBER_PHOTO__
\n__(Login)__ : __MEMBER_LOGIN__
\n__(Password)__ : __MEMBER_PASSWORD__
\n__(Phone)__ : __MEMBER_PHONE__
\n__(PhonePerso)__ : __MEMBER_PHONEPRO__
\n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingReminderForExpiredSubscription)',40,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(SubscriptionReminderEmail)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfSubscriptionReminderEmail)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnCancelation)' ,50,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasCanceled)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(YourMembershipWasCanceled)__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingAnEMailToMember)' ,60,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(CardContent)__', '__(Hello)__,

\n\n__(ThisIsContentOfYourCard)__
\n__(ID)__ : __ID__
\n__(Civiliyty)__ : __MEMBER_CIVILITY__
\n__(Firstname)__ : __MEMBER_FIRSTNAME__
\n__(Lastname)__ : __MEMBER_LASTNAME__
\n__(Fullname)__ : __MEMBER_FULLNAME__
\n__(Company)__ : __MEMBER_COMPANY__
\n__(Address)__ : __MEMBER_ADDRESS__
\n__(Zip)__ : __MEMBER_ZIP__
\n__(Town)__ : __MEMBER_TOWN__
\n__(Country)__ : __MEMBER_COUNTRY__
\n__(Email)__ : __MEMBER_EMAIL__
\n__(Birthday)__ : __MEMBER_BIRTH__
\n__(Photo)__ : __MEMBER_PHOTO__
\n__(Login)__ : __MEMBER_LOGIN__
\n__(Password)__ : __MEMBER_PASSWORD__
\n__(Phone)__ : __MEMBER_PHONE__
\n__(PhonePerso)__ : __MEMBER_PHONEPRO__
\n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); diff --git a/htdocs/install/mysql/migration/6.0.0-7.0.0.sql b/htdocs/install/mysql/migration/6.0.0-7.0.0.sql index 570bd340146..62d31da5c88 100644 --- a/htdocs/install/mysql/migration/6.0.0-7.0.0.sql +++ b/htdocs/install/mysql/migration/6.0.0-7.0.0.sql @@ -725,3 +725,6 @@ DROP TABLE llx_c_accountancy_category; UPDATE llx_cronjob set entity = 1 where entity = 0 and label in ('RecurringInvoices', 'SendEmailsReminders'); UPDATE llx_cronjob set entity = 0 where entity = 1 and label in ('PurgeDeleteTemporaryFilesShort', 'MakeLocalDatabaseDumpShort'); + +-- VMYSQL4.3 ALTER TABLE llx_c_shipment_mode MODIFY COLUMN tracking varchar(255) NULL; +-- VPGSQL8.2 ALTER TABLE llx_c_shipment_mode ALTER COLUMN tracking DROP NOT NULL; diff --git a/htdocs/install/mysql/migration/7.0.0-8.0.0.sql b/htdocs/install/mysql/migration/7.0.0-8.0.0.sql index f29830f29e0..1a7cfd58410 100644 --- a/htdocs/install/mysql/migration/7.0.0-8.0.0.sql +++ b/htdocs/install/mysql/migration/7.0.0-8.0.0.sql @@ -419,12 +419,12 @@ ALTER TABLE llx_societe_rib MODIFY COLUMN max_total_amount_of_all_payments doubl ALTER TABLE llx_societe_rib MODIFY COLUMN total_amount_of_all_payments double(24,8); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnAutoSubscription)' ,10,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipRequestWasReceived)__','__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourMembershipRequestWasReceived)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnMemberValidation)' ,20,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasValidated)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourMembershipWasValidated)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnAutoSubscription)' ,10,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipRequestWasReceived)__','__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourMembershipRequestWasReceived)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnMemberValidation)' ,20,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasValidated)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourMembershipWasValidated)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnNewSubscription)' ,30,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourSubscriptionWasRecorded)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfYourSubscriptionWasRecorded)__
\n\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingReminderForExpiredSubscription)',40,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(SubscriptionReminderEmail)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfSubscriptionReminderEmail)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnCancelation)' ,50,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasCanceled)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(YourMembershipWasCanceled)__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingAnEMailToMember)' ,60,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(CardContent)__', '__(Hello)__,

\n\n__(ThisIsContentOfYourCard)__
\n__(ID)__ : __ID__
\n__(Civiliyty)__ : __MEMBER_CIVILITY__
\n__(Firstname)__ : __MEMBER_FIRSTNAME__
\n__(Lastname)__ : __MEMBER_LASTNAME__
\n__(Fullname)__ : __MEMBER_FULLNAME__
\n__(Company)__ : __MEMBER_COMPANY__
\n__(Address)__ : __MEMBER_ADDRESS__
\n__(Zip)__ : __MEMBER_ZIP__
\n__(Town)__ : __MEMBER_TOWN__
\n__(Country)__ : __MEMBER_COUNTRY__
\n__(Email)__ : __MEMBER_EMAIL__
\n__(Birthday)__ : __MEMBER_BIRTH__
\n__(Photo)__ : __MEMBER_PHOTO__
\n__(Login)__ : __MEMBER_LOGIN__
\n__(Password)__ : __MEMBER_PASSWORD__
\n__(Phone)__ : __MEMBER_PHONE__
\n__(PhonePerso)__ : __MEMBER_PHONEPRO__
\n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

\n__(Sincerely)__
__USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingReminderForExpiredSubscription)',40,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(SubscriptionReminderEmail)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(ThisIsContentOfSubscriptionReminderEmail)__
\n
__ONLINE_PAYMENT_TEXT_AND_URL__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnCancelation)' ,50,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasCanceled)__', '__(Hello)__ __MEMBER_FULLNAME__,

\n\n__(YourMembershipWasCanceled)__
\n

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingAnEMailToMember)' ,60,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(CardContent)__', '__(Hello)__,

\n\n__(ThisIsContentOfYourCard)__
\n__(ID)__ : __ID__
\n__(Civiliyty)__ : __MEMBER_CIVILITY__
\n__(Firstname)__ : __MEMBER_FIRSTNAME__
\n__(Lastname)__ : __MEMBER_LASTNAME__
\n__(Fullname)__ : __MEMBER_FULLNAME__
\n__(Company)__ : __MEMBER_COMPANY__
\n__(Address)__ : __MEMBER_ADDRESS__
\n__(Zip)__ : __MEMBER_ZIP__
\n__(Town)__ : __MEMBER_TOWN__
\n__(Country)__ : __MEMBER_COUNTRY__
\n__(Email)__ : __MEMBER_EMAIL__
\n__(Birthday)__ : __MEMBER_BIRTH__
\n__(Photo)__ : __MEMBER_PHOTO__
\n__(Login)__ : __MEMBER_LOGIN__
\n__(Password)__ : __MEMBER_PASSWORD__
\n__(Phone)__ : __MEMBER_PHONE__
\n__(PhonePerso)__ : __MEMBER_PHONEPRO__
\n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

\n__(Sincerely)__
__USER_SIGNATURE__',null, 0); ALTER TABLE llx_product ADD COLUMN fk_default_warehouse integer DEFAULT NULL; ALTER TABLE llx_product ADD CONSTRAINT fk_product_default_warehouse FOREIGN KEY (fk_default_warehouse) REFERENCES llx_entrepot (rowid); diff --git a/htdocs/install/mysql/migration/8.0.0-9.0.0.sql b/htdocs/install/mysql/migration/8.0.0-9.0.0.sql index a7efecbdc5f..05106e475cd 100644 --- a/htdocs/install/mysql/migration/8.0.0-9.0.0.sql +++ b/htdocs/install/mysql/migration/8.0.0-9.0.0.sql @@ -137,3 +137,78 @@ CREATE TABLE llx_takepos_floor_tables( UPDATE llx_c_payment_term SET decalage = nbjour, nbjour = 0 where decalage IS NULL AND type_cdr = 2; UPDATE llx_holiday SET ref = rowid WHERE ref IS NULL; + + + +CREATE TABLE llx_emailcollector_emailcollector( + -- BEGIN MODULEBUILDER FIELDS + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + entity integer DEFAULT 1 NOT NULL, + ref varchar(128) NOT NULL, + label varchar(255), + description text, + host varchar(255), + user varchar(128), + password varchar(128), + source_directory varchar(255) NOT NULL, + target_directory varchar(255), + datelastresult datetime, + lastresult varchar(255), + note_public text, + note_private text, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer NOT NULL + -- END MODULEBUILDER FIELDS +) ENGINE=innodb; + +ALTER TABLE llx_emailcollector_emailcollector ADD INDEX idx_emailcollector_entity (entity); +ALTER TABLE llx_emailcollector_emailcollector ADD INDEX idx_emailcollector_status (status); + + +CREATE TABLE llx_emailcollector_emailcollectoraction( + -- BEGIN MODULEBUILDER FIELDS + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + fk_emailcollector INTEGER NOT NULL, + type varchar(128) NOT NULL, + actionparam varchar(255) NULL, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer NOT NULL + -- END MODULEBUILDER FIELDS +) ENGINE=innodb; + +CREATE TABLE llx_emailcollector_emailcollectorfilter( + -- BEGIN MODULEBUILDER FIELDS + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + fk_emailcollector INTEGER NOT NULL, + type varchar(128) NOT NULL, + rulevalue varchar(255) NULL, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer NOT NULL + -- END MODULEBUILDER FIELDS +) ENGINE=innodb; + +ALTER TABLE llx_emailcollector_emailcollectorfilter ADD INDEX idx_emailcollector_fk_emailcollector (fk_emailcollector); +ALTER TABLE llx_emailcollector_emailcollectorfilter ADD CONSTRAINT fk_emailcollectorfilter_fk_emailcollector FOREIGN KEY (fk_emailcollector) REFERENCES llx_emailcollector_emailcollector(rowid); + +ALTER TABLE llx_emailcollector_emailcollectoraction ADD INDEX idx_emailcollector_fk_emailcollector (fk_emailcollector); +ALTER TABLE llx_emailcollector_emailcollectoraction ADD CONSTRAINT fk_emailcollectoraction_fk_emailcollector FOREIGN KEY (fk_emailcollector) REFERENCES llx_emailcollector_emailcollector(rowid); + + +ALTER TABLE llx_emailcollector_emailcollectorfilter ADD UNIQUE INDEX uk_emailcollector_emailcollectorfilter (fk_emailcollector, type, rulevalue); +ALTER TABLE llx_emailcollector_emailcollectoraction ADD UNIQUE INDEX uk_emailcollector_emailcollectoraction (fk_emailcollector, type); + + + + diff --git a/htdocs/install/mysql/tables/llx_c_shipment_mode.sql b/htdocs/install/mysql/tables/llx_c_shipment_mode.sql index b94936c7461..7945c9f9fb0 100644 --- a/htdocs/install/mysql/tables/llx_c_shipment_mode.sql +++ b/htdocs/install/mysql/tables/llx_c_shipment_mode.sql @@ -23,7 +23,7 @@ create table llx_c_shipment_mode code varchar(30) NOT NULL, libelle varchar(50) NOT NULL, description text, - tracking varchar(255) NOT NULL, + tracking varchar(255) NULL, active tinyint DEFAULT 0, module varchar(32) NULL )ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_emailcollector_emailcollector.key.sql b/htdocs/install/mysql/tables/llx_emailcollector_emailcollector.key.sql new file mode 100644 index 00000000000..56295db116a --- /dev/null +++ b/htdocs/install/mysql/tables/llx_emailcollector_emailcollector.key.sql @@ -0,0 +1,25 @@ +-- Copyright (C) 2018 Laurent Destailleur +-- +-- 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 http://www.gnu.org/licenses/. + + +-- BEGIN MODULEBUILDER INDEXES +ALTER TABLE llx_emailcollector_emailcollector ADD INDEX idx_emailcollector_entity (entity); +ALTER TABLE llx_emailcollector_emailcollector ADD INDEX idx_emailcollector_status (status); +-- END MODULEBUILDER INDEXES + +--ALTER TABLE llx_emailcollector_emailcollector ADD UNIQUE INDEX uk_emailcollector_emailcollector_fieldxyz(fieldx, fieldy); + +--ALTER TABLE llx_emailcollector_emailcollector ADD CONSTRAINT llx_emailcollector_emailcollector_field_id FOREIGN KEY (fk_field) REFERENCES llx_myotherobject(rowid); + diff --git a/htdocs/install/mysql/tables/llx_emailcollector_emailcollector.sql b/htdocs/install/mysql/tables/llx_emailcollector_emailcollector.sql new file mode 100644 index 00000000000..2db5e693427 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_emailcollector_emailcollector.sql @@ -0,0 +1,40 @@ +-- Copyright (C) 2018 Laurent Destailleur +-- +-- 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 http://www.gnu.org/licenses/. + + +CREATE TABLE llx_emailcollector_emailcollector( + -- BEGIN MODULEBUILDER FIELDS + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + entity integer DEFAULT 1 NOT NULL, + ref varchar(128) NOT NULL, + label varchar(255), + description text, + host varchar(255), + user varchar(128), + password varchar(128), + source_directory varchar(255) NOT NULL, + target_directory varchar(255), + datelastresult datetime, + lastresult varchar(255), + note_public text, + note_private text, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer NOT NULL + -- END MODULEBUILDER FIELDS +) ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_emailcollector_emailcollectoraction.key.sql b/htdocs/install/mysql/tables/llx_emailcollector_emailcollectoraction.key.sql new file mode 100644 index 00000000000..ef8a91b5d31 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_emailcollector_emailcollectoraction.key.sql @@ -0,0 +1,22 @@ +-- Copyright (C) ---Put here your own copyright and developer email--- +-- +-- 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 http://www.gnu.org/licenses/. + + +-- BEGIN MODULEBUILDER INDEXES +ALTER TABLE llx_emailcollector_emailcollectoraction ADD INDEX idx_emailcollector_fk_emailcollector (fk_emailcollector); +ALTER TABLE llx_emailcollector_emailcollectoraction ADD CONSTRAINT fk_emailcollectoraction_fk_emailcollector FOREIGN KEY (fk_emailcollector) REFERENCES llx_emailcollector_emailcollector(rowid); +-- END MODULEBUILDER INDEXES + +ALTER TABLE llx_emailcollector_emailcollectoraction ADD UNIQUE INDEX uk_emailcollector_emailcollectoraction (fk_emailcollector, type); diff --git a/htdocs/install/mysql/tables/llx_emailcollector_emailcollectoraction.sql b/htdocs/install/mysql/tables/llx_emailcollector_emailcollectoraction.sql new file mode 100644 index 00000000000..82251f33706 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_emailcollector_emailcollectoraction.sql @@ -0,0 +1,30 @@ +-- Copyright (C) ---Put here your own copyright and developer email--- +-- +-- 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 http://www.gnu.org/licenses/. + + +CREATE TABLE llx_emailcollector_emailcollectoraction( + -- BEGIN MODULEBUILDER FIELDS + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + fk_emailcollector INTEGER NOT NULL, + type varchar(128) NOT NULL, + actionparam varchar(255) NULL, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer NOT NULL + -- END MODULEBUILDER FIELDS +) ENGINE=innodb; \ No newline at end of file diff --git a/htdocs/install/mysql/tables/llx_emailcollector_emailcollectorfilter.key.sql b/htdocs/install/mysql/tables/llx_emailcollector_emailcollectorfilter.key.sql new file mode 100644 index 00000000000..e0eb095fad1 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_emailcollector_emailcollectorfilter.key.sql @@ -0,0 +1,23 @@ +-- Copyright (C) ---Put here your own copyright and developer email--- +-- +-- 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 http://www.gnu.org/licenses/. + + +-- BEGIN MODULEBUILDER INDEXES +ALTER TABLE llx_emailcollector_emailcollectorfilter ADD INDEX idx_emailcollector_fk_emailcollector (fk_emailcollector); +ALTER TABLE llx_emailcollector_emailcollectorfilter ADD CONSTRAINT fk_emailcollectorfilter_fk_emailcollector FOREIGN KEY (fk_emailcollector) REFERENCES llx_emailcollector_emailcollector(rowid); +-- END MODULEBUILDER INDEXES + +ALTER TABLE llx_emailcollector_emailcollectorfilter ADD UNIQUE INDEX uk_emailcollector_emailcollectorfilter (fk_emailcollector, type, rulevalue); + diff --git a/htdocs/install/mysql/tables/llx_emailcollector_emailcollectorfilter.sql b/htdocs/install/mysql/tables/llx_emailcollector_emailcollectorfilter.sql new file mode 100644 index 00000000000..6312fa5785f --- /dev/null +++ b/htdocs/install/mysql/tables/llx_emailcollector_emailcollectorfilter.sql @@ -0,0 +1,30 @@ +-- Copyright (C) ---Put here your own copyright and developer email--- +-- +-- 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 http://www.gnu.org/licenses/. + + +CREATE TABLE llx_emailcollector_emailcollectorfilter( + -- BEGIN MODULEBUILDER FIELDS + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + fk_emailcollector INTEGER NOT NULL, + type varchar(128) NOT NULL, + rulevalue varchar(255) NULL, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer NOT NULL + -- END MODULEBUILDER FIELDS +) ENGINE=innodb; \ No newline at end of file diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 190b7cded97..d9be14b3cb7 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -1282,6 +1282,7 @@ AdherentLoginRequired= Manage a Login for each member AdherentMailRequired=EMail required to create a new member MemberSendInformationByMailByDefault=Checkbox to send mail confirmation to members (validation or new subscription) is on by default VisitorCanChooseItsPaymentMode=Visitor can choose among available payment modes +MEMBER_REMINDER_EMAIL=Enable automatic reminder by emails of expired subscriptions. Note: Module %s must be enabled and correctly setup to have reminder sent. ##### LDAP setup ##### LDAPSetup=LDAP Setup LDAPGlobalParameters=Global parameters @@ -1814,6 +1815,23 @@ EnableFeatureFor=Enable features for %s VATIsUsedIsOff=Note: The option to use sales Tax or VAT has been set to Off in the menu %s - %s, so Sale tax or Vat used will always be 0 for sales. SwapSenderAndRecipientOnPDF=Swap sender and recipient address on PDF FeatureSupportedOnTextFieldsOnly=Warning, feature supported on text fields only +EmailCollector=Email collector +EmailCollectorDescription=Add a scheduled job and a setup page to scan regularly email boxes (using IMAP protocol) and record emails received into your application, at the right place and/or create some record automatically (like leads). +NewEmailCollector=New Email Collector +EMailHost=Host of email IMAP server +MailboxSourceDirectory=Mailbox source directory +MailboxTargetDirectory=Mailbox target directory +EmailcollectorOperations=Operations done by collector +CollectNow=Collect now +DateLastResult=Date last collect +LastResult=Last result +EmailCollectorConfirmCollectTitle=Email collect confirmation +EmailCollectorConfirmCollect=Do you want to run the collect for this collector now ? +NoNewEmailToProcess=No new email to process +NothingProcessed=Nothing done +XEmailsDoneYActionsDone=%s emails analyzed, %s record/actions done by collector +RecordEvent=Record event +CreateLeadAndThirdParty=Create lead (and thirdparty if necessary) ##### Resource #### ResourceSetup=Configuration du module Resource UseSearchToSelectResource=Use a search form to choose a resource (rather than a drop-down list). diff --git a/htdocs/langs/en_US/assets.lang b/htdocs/langs/en_US/assets.lang index 0f955fe19ce..b780fb6fcba 100644 --- a/htdocs/langs/en_US/assets.lang +++ b/htdocs/langs/en_US/assets.lang @@ -22,7 +22,14 @@ AccountancyCodeAsset = Accounting code (asset) AccountancyCodeDepreciationAsset = Accounting code (depreciation asset account) AccountancyCodeDepreciationExpense = Accounting code (depreciation expense account) NewAssetType=New asset type +AssetsTypeSetup=Assets type setup +AssetTypeModified=Asset type modified +AssetType=Asset type AssetsLines=Assets +DeleteType=Delete +DeleteAnAssetType=Delete an asset type +ConfirmDeleteAssetType=Are you sure you want to delete this asset type? +ShowTypeCard=Show type '%s' # Module label 'ModuleAssetsName' ModuleAssetsName = Assets diff --git a/htdocs/langs/en_US/interventions.lang b/htdocs/langs/en_US/interventions.lang index 604d51c41b9..a130367b14f 100644 --- a/htdocs/langs/en_US/interventions.lang +++ b/htdocs/langs/en_US/interventions.lang @@ -4,6 +4,7 @@ Interventions=Interventions InterventionCard=Intervention card NewIntervention=New intervention AddIntervention=Create intervention +ChangeIntoRepeatableIntervention=Change to repeatable intervention ListOfInterventions=List of interventions ActionsOnFicheInter=Actions on intervention LastInterventions=Latest %s interventions diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index fa74cfa44bc..100a249f418 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -635,6 +635,8 @@ SendMail=Send email EMail=E-mail NoEMail=No email Email=Email +AlreadyRead=Alreay read +NotRead=Not read NoMobilePhone=No mobile phone Owner=Owner FollowingConstantsWillBeSubstituted=The following constants will be replaced with the corresponding value. diff --git a/htdocs/langs/es_ES/cron.lang b/htdocs/langs/es_ES/cron.lang index da8ac9c00aa..50497ad0b98 100644 --- a/htdocs/langs/es_ES/cron.lang +++ b/htdocs/langs/es_ES/cron.lang @@ -64,7 +64,7 @@ CronClassFile=Nombre de archivo con clase CronModuleHelp=Nombre del directorio del módulo Dolibarr (también funciona con módulos externos).
Por ejemplo, para realizar un fetch del objeto Product /htdocs/product/class/product.class.php, el valor del módulo es product CronClassFileHelp=El nombre del archivo a cargar (la ruta es relativa al directorio raiz del servidor web).
Por ejemplo para realizar un fetch del objeto Product /htdocs/product/class/product.class.php, el valor del nombre del archivo de la clase es product.class.php CronObjectHelp=El nombre del objeto a cargar.
Por ejemplo para realizar un fetch del objeto Product /htdocs/product/class/product.class.php, el valor del nombre de la clase es Product -CronMethodHelp=El método del objeto a lanzar.
Por ejemplo para realizar un fetch del objeto Product /htdocs/product/class/product.class.php, el valor del método es fecth +CronMethodHelp=El método del objeto a lanzar.
Por ejemplo para realizar un fetch del objeto Product /htdocs/product/class/product.class.php, el valor del método es fetch CronArgsHelp=Los argumentos del método.
Por ejemplo para realizar un fetch del objeto Product /htdocs/product/class/product.class.php, los valores pueden ser 0, ProductRef CronCommandHelp=El comando en línea del sistema a ejecutar. CronCreateJob=Crear nueva tarea programada diff --git a/htdocs/langs/fr_FR/members.lang b/htdocs/langs/fr_FR/members.lang index 6cd379c3cc2..a27cd5c5747 100644 --- a/htdocs/langs/fr_FR/members.lang +++ b/htdocs/langs/fr_FR/members.lang @@ -111,7 +111,7 @@ SendingAnEMailToMember=Envoi d'informations par e-mail à un adhérent SendingEmailOnAutoSubscription=Envoi d'email lors de l'auto-inscription SendingEmailOnMemberValidation=Envoie d'email à la validation d'un nouvel adhérent SendingEmailOnNewSubscription=Envoyer un email sur un nouvel abonnement -SendingReminderForExpiredSubscription=Envoi d'un rappel pour les abonnements expirés +SendingReminderForExpiredSubscription=Envoi d'un rappel pour les adhésions expirées SendingEmailOnCancelation=Envoie d'email à l'annulation # Topic of email templates YourMembershipRequestWasReceived=Votre demande d'adhésion a été reçue. @@ -124,7 +124,7 @@ CardContent=Contenu de votre fiche adhérent ThisIsContentOfYourMembershipRequestWasReceived=Nous vous informons que votre demande d'adhésion a bien été reçue.

ThisIsContentOfYourMembershipWasValidated=Nous vous informons que votre adhésion a été validé avec les informations suivantes:

ThisIsContentOfYourSubscriptionWasRecorded=Nous vous informons que votre nouvelle cotisation a été enregistrée.

-ThisIsContentOfSubscriptionReminderEmail=Nous voulons vous informer que votre abonnement est sur le point d'expirer. Nous espérons que vous pourrez le renouveler.

+ThisIsContentOfSubscriptionReminderEmail=Nous voulons vous informer que votre adhésion est sur le point d'expirer. Nous espérons que vous pourrez la renouveler, votre soutien nous ait précieux

ThisIsContentOfYourCard=Ceci est un rappel des informations que nous avons vos concernant. N'hésitez pas à nous contacter en cas d'erreur.

DescADHERENT_AUTOREGISTER_NOTIF_MAIL_SUBJECT=Sujet de l'email reçu en cas d'auto-inscription d'un invité DescADHERENT_AUTOREGISTER_NOTIF_MAIL=Email reçu en cas d'auto-inscription d'un invité diff --git a/htdocs/loan/card.php b/htdocs/loan/card.php index e2491167ed8..242472aff0a 100644 --- a/htdocs/loan/card.php +++ b/htdocs/loan/card.php @@ -264,7 +264,7 @@ if ($action == 'create') print ''; // Label - print ''; + print ''; // Bank account if (! empty($conf->banque->enabled)) @@ -434,7 +434,7 @@ if ($id > 0) // Loan card - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; $morehtmlref='
'; // Ref loan diff --git a/htdocs/loan/document.php b/htdocs/loan/document.php index 2c762b624d2..553bbc258e8 100644 --- a/htdocs/loan/document.php +++ b/htdocs/loan/document.php @@ -124,7 +124,7 @@ if ($object->id) } $morehtmlref.='
'; - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; $object->totalpaid = $totalpaid; // To give a chance to dol_banner_tab to use already paid amount to show correct status diff --git a/htdocs/loan/info.php b/htdocs/loan/info.php index 3a092e4cb84..cc923b17fea 100644 --- a/htdocs/loan/info.php +++ b/htdocs/loan/info.php @@ -96,7 +96,7 @@ if (! empty($conf->projet->enabled)) { } $morehtmlref.=''; -$linkback = '' . $langs->trans("BackToList") . ''; +$linkback = '' . $langs->trans("BackToList") . ''; $object->totalpaid = $totalpaid; // To give a chance to dol_banner_tab to use already paid amount to show correct status diff --git a/htdocs/loan/index.php b/htdocs/loan/list.php similarity index 99% rename from htdocs/loan/index.php rename to htdocs/loan/list.php index 5b81ebbb023..780d28df081 100644 --- a/htdocs/loan/index.php +++ b/htdocs/loan/list.php @@ -19,7 +19,7 @@ */ /** - * \file htdocs/loan/index.php + * \file htdocs/loan/list.php * \ingroup loan * \brief Page to list all loans */ diff --git a/htdocs/loan/note.php b/htdocs/loan/note.php index fea8388979b..e066ef07a8e 100644 --- a/htdocs/loan/note.php +++ b/htdocs/loan/note.php @@ -113,7 +113,7 @@ if ($id > 0) } $morehtmlref.=''; - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; $object->totalpaid = $totalpaid; // To give a chance to dol_banner_tab to use already paid amount to show correct status diff --git a/htdocs/loan/payment/card.php b/htdocs/loan/payment/card.php index 919139bd6e3..77d47e66b40 100644 --- a/htdocs/loan/payment/card.php +++ b/htdocs/loan/payment/card.php @@ -58,7 +58,7 @@ if ($action == 'confirm_delete' && $confirm == 'yes' && $user->rights->loan->del if ($result > 0) { $db->commit(); - header("Location: ".DOL_URL_ROOT."/loan/index.php"); + header("Location: ".DOL_URL_ROOT."/loan/list.php"); exit; } else diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index 20eee6e496e..06f67a9b79f 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -1382,7 +1382,6 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs } else { - if (! preg_match('/^\//',$jsfile)) $jsfile='/'.$jsfile; // For backward compatibility print ''."\n"; } } diff --git a/htdocs/margin/tabs/thirdpartyMargins.php b/htdocs/margin/tabs/thirdpartyMargins.php index 8ce4b7a0c0b..55362ac4218 100644 --- a/htdocs/margin/tabs/thirdpartyMargins.php +++ b/htdocs/margin/tabs/thirdpartyMargins.php @@ -203,6 +203,11 @@ if ($socid > 0) $marginRate = ($objp->buying_price != 0)?(100 * $objp->marge / $objp->buying_price):'' ; $markRate = ($objp->selling_price != 0)?(100 * $objp->marge / $objp->selling_price):'' ; + $sign = ''; + if ($objp->type == Facture::TYPE_CREDIT_NOTE){ + $sign = '-'; + } + print ''; print '"; print "\n"; print "\n"; - print "\n"; + print "\n"; if (! empty($conf->global->DISPLAY_MARGIN_RATES)) - print "\n"; + print "\n"; if (! empty($conf->global->DISPLAY_MARK_RATES)) - print "\n"; + print "\n"; print ''; print "\n"; $i++; diff --git a/htdocs/modulebuilder/index.php b/htdocs/modulebuilder/index.php index b533c369cbd..24596255043 100644 --- a/htdocs/modulebuilder/index.php +++ b/htdocs/modulebuilder/index.php @@ -35,7 +35,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php'; // Load translation files required by the page -$langs->loadLangs(array("admin", "modulebuilder", "other", "cron")); +$langs->loadLangs(array("admin", "modulebuilder", "other", "cron", "errors")); $action=GETPOST('action','aZ09'); $confirm=GETPOST('confirm','alpha'); @@ -71,6 +71,11 @@ if (! empty($tmpdir[1])) $dirread=$tmpdir[1]; $forceddirread=1; } +if (GETPOST('dirins','alpha')) +{ + $dirread = $dirins = GETPOST('dirins','alpha'); + $forceddirread=1; +} $FILEFLAG='modulebuilder.txt'; @@ -911,17 +916,31 @@ $dirsincustom=dol_dir_list($dirread, 'directories'); if (is_array($dirsincustom) && count($dirsincustom) > 0) { foreach ($dirsincustom as $dircustomcursor) { $fullname = $dircustomcursor['fullname']; - if (dol_is_file($fullname . '/' . $FILEFLAG)) { + if (dol_is_file($fullname . '/' . $FILEFLAG)) + { // Get real name of module (MyModule instead of mymodule) - $descriptorfiles = dol_dir_list($fullname . '/core/modules/', 'files', 0, 'mod.*\.class\.php$'); + $dirtoscanrel = basename($fullname).'/core/modules/'; + + $descriptorfiles = dol_dir_list(dirname($fullname).'/'.$dirtoscanrel, 'files', 0, 'mod.*\.class\.php$'); + if (empty($descriptorfiles)) // If descriptor not found into module dir, we look into main module dir. + { + $dirtoscanrel = 'core/modules/'; + $descriptorfiles = dol_dir_list($fullname.'/../'.$dirtoscanrel, 'files', 0, 'mod'.strtoupper(basename($fullname)).'\.class\.php$'); + } $modulenamewithcase = ''; + $moduledescriptorrelpath = ''; + $moduledescriptorfullpath = ''; + foreach ($descriptorfiles as $descriptorcursor) { $modulenamewithcase = preg_replace('/^mod/', '', $descriptorcursor['name']); $modulenamewithcase = preg_replace('/\.class\.php$/', '', $modulenamewithcase); + $moduledescriptorrelpath = $dirtoscanrel.$descriptorcursor['name']; + $moduledescriptorfullpath = $descriptorcursor['fullname']; + //var_dump($descriptorcursor); } if ($modulenamewithcase) { - $listofmodules[$dircustomcursor['name']] = $modulenamewithcase; + $listofmodules[$dircustomcursor['name']] = array('modulenamewithcase'=>$modulenamewithcase, 'moduledescriptorrelpath'=> $moduledescriptorrelpath, 'moduledescriptorfullpath'=>$moduledescriptorfullpath); } //var_dump($listofmodules); } @@ -929,7 +948,7 @@ if (is_array($dirsincustom) && count($dirsincustom) > 0) { } if ($forceddirread && empty($listofmodules)) { - $listofmodules[strtolower($module)] = $module; + $listofmodules[strtolower($module)] = array('modulenamewithcase'=>$module, 'moduledescriptorrelpath'=> 'notyetimplemented', 'moduledescriptorfullpath'=> 'notyetimplemented'); } // Show description of content @@ -983,7 +1002,8 @@ if (! empty($module) && $module != 'initmodule' && $module != 'deletemodule') $modulelowercase=strtolower($module); // Load module - dol_include_once($modulelowercase.'/core/modules/mod'.$module.'.class.php'); + $fullpathdirtodescriptor = $listofmodules[strtolower($module)]['moduledescriptorrelpath']; + dol_include_once($fullpathdirtodescriptor); $class='mod'.$module; if (class_exists($class)) @@ -1017,11 +1037,11 @@ $head[$h][1] = $langs->trans("NewModule"); $head[$h][2] = 'initmodule'; $h++; -foreach($listofmodules as $tmpmodule => $tmpmodulewithcase) +foreach($listofmodules as $tmpmodule => $tmpmodulearray) { - $head[$h][0] = $_SERVER["PHP_SELF"].'?module='.$tmpmodulewithcase.($forceddirread?'@'.$dirread:''); - $head[$h][1] = $tmpmodulewithcase; - $head[$h][2] = $tmpmodulewithcase; + $head[$h][0] = $_SERVER["PHP_SELF"].'?module='.$tmpmodulearray['modulenamewithcase'].($forceddirread?'@'.$dirread:''); + $head[$h][1] = $tmpmodulearray['modulenamewithcase']; + $head[$h][2] = $tmpmodulearray['modulenamewithcase']; $h++; } @@ -1167,7 +1187,7 @@ elseif (! empty($module)) if ($tab == 'description') { - $pathtofile = $modulelowercase.'/core/modules/mod'.$module.'.class.php'; + $pathtofile = $listofmodules[strtolower($module)]['moduledescriptorrelpath']; $pathtofilereadme = $modulelowercase.'/README.md'; $pathtochangelog = $modulelowercase.'/ChangeLog.md'; @@ -1260,8 +1280,8 @@ elseif (! empty($module)) print '
'; print '
'; - - print $moduleobj->getDescLong(); + if (dol_is_file($dirread.'/'.$pathtofilereadme)) print $moduleobj->getDescLong(); + else print $langs->trans("ErrorFileNotFound", $pathtofilereadme); print '

'; @@ -1271,7 +1291,8 @@ elseif (! empty($module)) print '
'; print '
'; - print $moduleobj->getChangeLog(); + if (dol_is_file($dirread.'/'.$pathtochangelog)) print $moduleobj->getChangeLog(); + else print $langs->trans("ErrorFileNotFound", $pathtochangelog); print '
'; } @@ -1542,6 +1563,8 @@ elseif (! empty($module)) if ($action != 'editfile' || empty($file)) { try { + //$pathtofile = $listofmodules[strtolower($module)]['moduledescriptorrelpath']; + $pathtoclass = strtolower($module).'/class/'.strtolower($tabobj).'.class.php'; $pathtoapi = strtolower($module).'/class/api_'.strtolower($module).'.class.php'; $pathtoagenda = strtolower($module).'/'.strtolower($tabobj).'_agenda.php'; @@ -1557,20 +1580,22 @@ elseif (! empty($module)) $pathtopicto = strtolower($module).'/img/object_'.strtolower($tabobj).'.png'; $pathtoscript = strtolower($module).'/scripts/'.strtolower($tabobj).'.php'; - $realpathtoclass = dol_buildpath($pathtoclass, 0, 1); - $realpathtoapi = dol_buildpath($pathtoapi, 0, 1); - $realpathtoagenda = dol_buildpath($pathtoagenda, 0, 1); - $realpathtocard = dol_buildpath($pathtocard, 0, 1); - $realpathtodocument = dol_buildpath($pathtodocument, 0, 1); - $realpathtolist = dol_buildpath($pathtolist, 0, 1); - $realpathtonote = dol_buildpath($pathtonote, 0, 1); - $realpathtophpunit = dol_buildpath($pathtophpunit, 0, 1); - $realpathtosql = dol_buildpath($pathtosql, 0, 1); - $realpathtosqlextra = dol_buildpath($pathtosqlextra, 0, 1); - $realpathtosqlkey = dol_buildpath($pathtosqlkey, 0, 1); - $realpathtolib = dol_buildpath($pathtolib, 0, 1); - $realpathtopicto = dol_buildpath($pathtopicto, 0, 1); - $realpathtoscript = dol_buildpath($pathtoscript, 0, 1); + //var_dump($pathtolib); + + $realpathtoclass = dol_buildpath($pathtoclass, 0, 2); + $realpathtoapi = dol_buildpath($pathtoapi, 0, 2); + $realpathtoagenda = dol_buildpath($pathtoagenda, 0, 2); + $realpathtocard = dol_buildpath($pathtocard, 0, 2); + $realpathtodocument = dol_buildpath($pathtodocument, 0, 2); + $realpathtolist = dol_buildpath($pathtolist, 0, 2); + $realpathtonote = dol_buildpath($pathtonote, 0, 2); + $realpathtophpunit = dol_buildpath($pathtophpunit, 0, 2); + $realpathtosql = dol_buildpath($pathtosql, 0, 2); + $realpathtosqlextra = dol_buildpath($pathtosqlextra, 0, 2); + $realpathtosqlkey = dol_buildpath($pathtosqlkey, 0, 2); + $realpathtolib = dol_buildpath($pathtolib, 0, 2); + $realpathtopicto = dol_buildpath($pathtopicto, 0, 2); + $realpathtoscript = dol_buildpath($pathtoscript, 0, 2); print '
'; print ' '.$langs->trans("ClassFile").' : '.($realpathtoclass?'':'').$pathtoclass.($realpathtoclass?'':'').''; @@ -1877,7 +1902,7 @@ elseif (! empty($module)) if ($tab == 'menus') { - $pathtofile = $modulelowercase.'/core/modules/mod'.$module.'.class.php'; + $pathtofile = $listofmodules[strtolower($module)]['moduledescriptorrelpath']; //$menus = $moduleobj->; @@ -2002,7 +2027,7 @@ elseif (! empty($module)) if ($tab == 'permissions') { - $pathtofile = $modulelowercase.'/core/modules/mod'.$module.'.class.php'; + $pathtofile = $listofmodules[strtolower($module)]['moduledescriptorrelpath']; //$perms = $moduleobj->; @@ -2132,7 +2157,7 @@ elseif (! empty($module)) print $langs->trans("HooksDefDesc").'
'; print '
'; - $pathtofile = $modulelowercase.'/core/modules/mod'.$module.'.class.php'; + $pathtofile = $listofmodules[strtolower($module)]['moduledescriptorrelpath']; print ' '.$langs->trans("DescriptorFile").' : '.$pathtofile.''; print ' '.img_picto($langs->trans("Edit"), 'edit').''; print '
'; @@ -2277,7 +2302,7 @@ elseif (! empty($module)) if ($tab == 'cron') { - $pathtofile = $modulelowercase.'/core/modules/mod'.$module.'.class.php'; + $pathtofile = $listofmodules[strtolower($module)]['moduledescriptorrelpath']; $cronjobs = $moduleobj->cronjobs; @@ -2411,7 +2436,8 @@ elseif (! empty($module)) $FILENAMEZIP=''; // Load module - dol_include_once($modulelowercase.'/core/modules/mod'.$module.'.class.php'); + $pathtofile = $listofmodules[strtolower($module)]['moduledescriptorrelpath']; + dol_include_once($pathtofile); $class='mod'.$module; if (class_exists($class)) diff --git a/htdocs/modulebuilder/template/class/myobject.class.php b/htdocs/modulebuilder/template/class/myobject.class.php index f5f3f582fbc..6e3fc1bd63f 100644 --- a/htdocs/modulebuilder/template/class/myobject.class.php +++ b/htdocs/modulebuilder/template/class/myobject.class.php @@ -98,7 +98,7 @@ class MyObject extends CommonObject 'fk_user_modif' =>array('type'=>'integer', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>511), //'fk_user_valid' =>array('type'=>'integer', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>512), 'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'index'=>0, 'position'=>1000), - 'status' =>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')), + 'status' =>array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'default'=>0, 'index'=>1, 'position'=>1000, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Active', -1=>'Canceled')), ); /** @@ -198,8 +198,17 @@ class MyObject extends CommonObject } } - // Translate some data - $this->fields['status']['arrayofkeyval']=array(0=>$langs->trans('Draft'), 1=>$langs->trans('Active'), -1=>$langs->trans('Cancel')); + // Translate some data of arrayofkeyval + foreach($this->fields as $key => $val) + { + if (is_array($this->fields['status']['arrayofkeyval'])) + { + foreach($this->fields['status']['arrayofkeyval'] as $key2 => $val2) + { + $this->fields['status']['arrayofkeyval'][$key2]=$langs->trans($val2); + } + } + } } /** @@ -348,7 +357,6 @@ class MyObject extends CommonObject if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips $result = ''; - $companylink = ''; $label = '' . $langs->trans("MyObject") . ''; $label.= '
'; @@ -410,12 +418,12 @@ class MyObject extends CommonObject * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto * @return string Label of status */ - function getLibStatut($mode=0) + public function getLibStatut($mode=0) { return $this->LibStatut($this->status, $mode); } - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Return the status * @@ -423,7 +431,7 @@ class MyObject extends CommonObject * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto * @return string Label of status */ - function LibStatut($status, $mode=0) + public function LibStatut($status, $mode=0) { // phpcs:enable if (empty($this->labelstatus)) @@ -444,28 +452,28 @@ class MyObject extends CommonObject } elseif ($mode == 2) { - if ($status == 1) return img_picto($this->labelstatus[$status],'statut4').' '.$this->labelstatus[$status]; - elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5').' '.$this->labelstatus[$status]; + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; } elseif ($mode == 3) { - if ($status == 1) return img_picto($this->labelstatus[$status],'statut4'); - elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5'); + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); } elseif ($mode == 4) { - if ($status == 1) return img_picto($this->labelstatus[$status],'statut4').' '.$this->labelstatus[$status]; - elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5').' '.$this->labelstatus[$status]; + if ($status == 1) return img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; + elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status]; } elseif ($mode == 5) { - if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4'); - elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5'); + if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); } elseif ($mode == 6) { - if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4'); - elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5'); + if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut4', '', false, 0, 0, '', 'valignmiddle'); + elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut5', '', false, 0, 0, '', 'valignmiddle'); } } @@ -475,7 +483,7 @@ class MyObject extends CommonObject * @param int $id Id of order * @return void */ - function info($id) + public function info($id) { $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; $sql.= ' fk_user_creat, fk_user_modif'; diff --git a/htdocs/modulebuilder/template/core/modules/modMyModule.class.php b/htdocs/modulebuilder/template/core/modules/modMyModule.class.php index d638ee874fd..8d34b0e63a3 100644 --- a/htdocs/modulebuilder/template/core/modules/modMyModule.class.php +++ b/htdocs/modulebuilder/template/core/modules/modMyModule.class.php @@ -63,7 +63,7 @@ class modMyModule extends DolibarrModules // Module description, used if translation string 'ModuleMyModuleDesc' not found (MyModule is name of module). $this->description = "MyModuleDescription"; // Used only if file README.md and README-LL.md not found. - $this->descriptionlong = "MyModuleDescription (Long)"; + $this->descriptionlong = "MyModule description (Long)"; $this->editor_name = 'Editor name'; $this->editor_url = 'https://www.example.com'; @@ -80,10 +80,7 @@ class modMyModule extends DolibarrModules // If file is in module/img directory under name object_pictovalue.png, use this->picto='pictovalue@module' $this->picto='generic'; - // Defined all module parts (triggers, login, substitutions, menus, css, etc...) - // for default path (eg: /mymodule/core/xxxxx) (0=disable, 1=enable) - // for specific path of parts (eg: /mymodule/core/modules/barcode) - // for specific css file (eg: /mymodule/css/mymodule.css.php) + // Define some features supported by module (triggers, login, substitutions, menus, css, etc...) $this->module_parts = array( 'triggers' => 1, // Set this to 1 if module has its own trigger directory (core/triggers) 'login' => 0, // Set this to 1 if module has its own login method file (core/login) @@ -108,14 +105,14 @@ class modMyModule extends DolibarrModules // Dependencies $this->hidden = false; // A condition to hide module - $this->depends = array(); // List of module class names as string that must be enabled if this module is enabled. array('always1'=>'modModule1','always2'=>'modModule2', 'FR1'=>'modModuleFR'...) - $this->requiredby = array(); // List of module class names to disable if this one is disabled - $this->conflictwith = array(); // List of module class names as string this module is in conflict with + $this->depends = array(); // List of module class names as string that must be enabled if this module is enabled. Example: array('always1'=>'modModuleToEnable1','always2'=>'modModuleToEnable2', 'FR1'=>'modModuleToEnableFR'...) + $this->requiredby = array(); // List of module class names as string to disable if this one is disabled. Example: array('modModuleToDisable1', ...) + $this->conflictwith = array(); // List of module class names as string this module is in conflict with. Example: array('modModuleToDisable1', ...) $this->langfiles = array("mymodule@mymodule"); - $this->phpmin = array(5,4); // Minimum version of PHP required by module - $this->need_dolibarr_version = array(4,0); // Minimum version of Dolibarr required by module - $this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...) - $this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...) + //$this->phpmin = array(5,4); // Minimum version of PHP required by module + $this->need_dolibarr_version = array(4,0); // Minimum version of Dolibarr required by module + $this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...) + $this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...) //$this->automatic_activation = array('FR'=>'MyModuleWasAutomaticallyActivatedBecauseOfYourCountryChoice'); //$this->always_enabled = true; // If true, can't be disabled diff --git a/htdocs/modulebuilder/template/myobject_card.php b/htdocs/modulebuilder/template/myobject_card.php index 81715228033..f9b3db7a99e 100644 --- a/htdocs/modulebuilder/template/myobject_card.php +++ b/htdocs/modulebuilder/template/myobject_card.php @@ -68,7 +68,7 @@ $langs->loadLangs(array("mymodule@mymodule","other")); // Get parameters $id = GETPOST('id', 'int'); $ref = GETPOST('ref', 'alpha'); -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'aZ09'); $confirm = GETPOST('confirm', 'alpha'); $cancel = GETPOST('cancel', 'aZ09'); $contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'myobjectcard'; // To manage different context of search @@ -81,7 +81,7 @@ $diroutputmassaction=$conf->mymodule->dir_output . '/temp/massgeneration/'.$user $hookmanager->initHooks(array('myobjectcard','globalcard')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Initialize array of search criterias $search_all=trim(GETPOST("search_all",'alpha')); @@ -347,7 +347,6 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea print '
'.$langs->trans("Label").'
'.$langs->trans("Label").'
'; $invoicestatic->id=$objp->facid; @@ -213,11 +218,11 @@ if ($socid > 0) print dol_print_date($db->jdate($objp->datef),'day')."".price($objp->selling_price, null, null, null, null, $rounding)."".price(($objp->type == 2 ? -1 : 1) * $objp->buying_price, null, null, null, null, $rounding)."".price($objp->marge, null, null, null, null, $rounding)."".$sign.price($objp->marge, null, null, null, null, $rounding)."".(($marginRate === '')?'n/a':price($marginRate, null, null, null, null, $rounding)."%")."".(($marginRate === '')?'n/a':$sign.price($marginRate, null, null, null, null, $rounding)."%")."".(($markRate === '')?'n/a':price($markRate, null, null, null, null, $rounding)."%")."".(($markRate === '')?'n/a':price($markRate, null, null, null, null, $rounding)."%")."'.$invoicestatic->LibStatut($objp->paye,$objp->statut,5).'
'; print '
'; print '
'; - print '
'; print '

'; diff --git a/htdocs/modulebuilder/template/myobject_list.php b/htdocs/modulebuilder/template/myobject_list.php index ece26aba6cb..8d0f89104ef 100644 --- a/htdocs/modulebuilder/template/myobject_list.php +++ b/htdocs/modulebuilder/template/myobject_list.php @@ -93,8 +93,8 @@ $extrafields = new ExtraFields($db); $diroutputmassaction=$conf->mymodule->dir_output . '/temp/massgeneration/'.$user->id; $hookmanager->initHooks(array('myobjectlist')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels -$extralabels = $extrafields->fetch_name_optionals_label('myobject'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$extralabels = $extrafields->fetch_name_optionals_label('myobject'); // Load $extrafields->attributes['myobject'] +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Default sort order (if not yet defined by previous GETPOST) if (! $sortfield) $sortfield="t.".key($object->fields); // Set here default search field. By default 1st field in definition. @@ -292,7 +292,7 @@ if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && { $obj = $db->fetch_object($resql); $id = $obj->rowid; - header("Location: ".DOL_URL_ROOT.'/mymodule/myobject_card.php?id='.$id); + header("Location: ".dol_buildpath('/mymodule/myobject_card.php', 1).'?id='.$id); exit; } @@ -352,7 +352,7 @@ print ''; $newcardbutton=''; //if ($user->rights->mymodule->creer) //{ - $newcardbutton=''.$langs->trans('New').''; + $newcardbutton=''.$langs->trans('New').''; $newcardbutton.= ''; $newcardbutton.= ''; //} diff --git a/htdocs/product/class/api_products.class.php b/htdocs/product/class/api_products.class.php index ce6928c7331..30940c95509 100644 --- a/htdocs/product/class/api_products.class.php +++ b/htdocs/product/class/api_products.class.php @@ -46,44 +46,52 @@ class Products extends DolibarrApi */ function __construct() { - global $db, $conf; - $this->db = $db; + global $db, $conf; + $this->db = $db; $this->product = new Product($this->db); } /** - * Get properties of a product object + * Get properties of a product object (from its ID, Ref, Ref_ext or Barcode) * * Return an array with product information. * TODO implement getting a product by ref or by $ref_ext * - * @param int $id ID of product - * @param int $includestockdata Load also information about stock (slower) - * @return array|mixed Data without useless information - * + * @param int $id ID of product + * @param string $ref Ref of element + * @param string $ref_ext Ref ext of element + * @param string $barcode Barcode of element + * @param int $includestockdata Load also information about stock (slower) + * @return array|mixed Data without useless information + * * @throws 401 * @throws 403 * @throws 404 */ - function get($id, $includestockdata=0) + function get($id, $ref='', $ref_ext='', $barcode='', $includestockdata=0) { - if(! DolibarrApiAccess::$user->rights->produit->lire) { - throw new RestException(403); - } - - $result = $this->product->fetch($id); - if( ! $result ) { - throw new RestException(404, 'Product not found'); + if (empty($id) && empty($ref) && empty($ref_ext) && empty($barcode)) { + throw new RestException(400, 'bad value for parameter id, ref, ref_ext or barcode'); } - if( ! DolibarrApi::_checkAccessToResource('product',$this->product->id)) { - throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); - } + $id = (empty($id)?0:$id); + + if(! DolibarrApiAccess::$user->rights->produit->lire) { + throw new RestException(403); + } - if ($includestockdata) - { - $this->product->load_stock(); - } + $result = $this->product->fetch($id, $ref, $ref_ext, $barcode); + if(! $result ) { + throw new RestException(404, 'Product not found'); + } + + if(! DolibarrApi::_checkAccessToResource('product', $this->product->id)) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + if ($includestockdata) { + $this->product->load_stock(); + } return $this->_cleanObjectDatas($this->product); } @@ -93,13 +101,13 @@ class Products extends DolibarrApi * * Get a list of products * - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Limit for list - * @param int $page Page number - * @param int $mode Use this param to filter list (0 for all, 1 for only product, 2 for only service) - * @param int $category Use this param to filter list by category - * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.tobuy:=:0) and (t.tosell:=:1)" + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Limit for list + * @param int $page Page number + * @param int $mode Use this param to filter list (0 for all, 1 for only product, 2 for only service) + * @param int $category Use this param to filter list by category + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.tobuy:=:0) and (t.tosell:=:1)" * @return array Array of product objects */ function index($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $mode=0, $category=0, $sqlfilters = '') @@ -112,36 +120,33 @@ class Products extends DolibarrApi $sql = "SELECT t.rowid, t.ref, t.ref_ext"; $sql.= " FROM ".MAIN_DB_PREFIX."product as t"; - if ($category > 0) - { + if ($category > 0) { $sql.= ", ".MAIN_DB_PREFIX."categorie_product as c"; } $sql.= ' WHERE t.entity IN ('.getEntity('product').')'; // Select products of given category - if ($category > 0) - { + if ($category > 0) { $sql.= " AND c.fk_categorie = ".$db->escape($category); $sql.= " AND c.fk_product = t.rowid "; } // Show products - if ($mode == 1) $sql.= " AND t.fk_product_type = 0"; + if ($mode == 1) { $sql.= " AND t.fk_product_type = 0"; + } // Show services - if ($mode == 2) $sql.= " AND t.fk_product_type = 1"; + if ($mode == 2) { $sql.= " AND t.fk_product_type = 1"; + } // Add sql filters - if ($sqlfilters) - { - if (! DolibarrApi::_checkFilters($sqlfilters)) - { + if ($sqlfilters) { + if (! DolibarrApi::_checkFilters($sqlfilters)) { throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); } - $regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; $sql.=" AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; } $sql.= $db->order($sortfield, $sortorder); - if ($limit) { - if ($page < 0) - { + if ($limit) { + if ($page < 0) { $page = 0; } $offset = $limit * $page; @@ -150,8 +155,7 @@ class Products extends DolibarrApi } $result = $db->query($sql); - if ($result) - { + if ($result) { $num = $db->num_rows($result); $min = min($num, ($limit <= 0 ? $num : $limit)); while ($i < $min) @@ -167,7 +171,7 @@ class Products extends DolibarrApi else { throw new RestException(503, 'Error when retrieve product list : '.$db->lasterror()); } - if( ! count($obj_ret)) { + if(! count($obj_ret)) { throw new RestException(404, 'No product found'); } return $obj_ret; @@ -176,14 +180,14 @@ class Products extends DolibarrApi /** * Create product object * - * @param array $request_data Request data - * @return int ID of product + * @param array $request_data Request data + * @return int ID of product */ function post($request_data = null) { if(! DolibarrApiAccess::$user->rights->produit->creer) { - throw new RestException(401); - } + throw new RestException(401); + } // Check mandatory fields $result = $this->_validate($request_data); @@ -201,8 +205,8 @@ class Products extends DolibarrApi * Update product. * Price will be updated by this API only if option is set on "One price per product". See other APIs for other price modes. * - * @param int $id Id of product to update - * @param array $request_data Datas + * @param int $id Id of product to update + * @param array $request_data Datas * @return int * * @throws RestException @@ -211,99 +215,102 @@ class Products extends DolibarrApi */ function put($id, $request_data = null) { - global $conf; + global $conf; if(! DolibarrApiAccess::$user->rights->produit->creer) { - throw new RestException(401); - } + throw new RestException(401); + } $result = $this->product->fetch($id); - if( ! $result ) { + if(! $result ) { throw new RestException(404, 'Product not found'); } - if( ! DolibarrApi::_checkAccessToResource('product',$this->product->id)) { - throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); - } + if(! DolibarrApi::_checkAccessToResource('product', $this->product->id)) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } - $oldproduct = dol_clone($this->product, 0); + $oldproduct = dol_clone($this->product, 0); foreach($request_data as $field => $value) { - if ($field == 'id') continue; + if ($field == 'id') { continue; + } $this->product->$field = $value; } $result = $this->product->update($id, DolibarrApiAccess::$user, 1, 'update'); // If price mode is 1 price per product - if ($result > 0 && ! empty($conf->global->PRODUCT_PRICE_UNIQ)) - { - // We update price only if it was changed - $pricemodified = false; - if ($this->product->price_base_type != $oldproduct->price_base_type) $pricemodified = true; - else - { - if ($this->product->tva_tx != $oldproduct->tva_tx) $pricemodified = true; - if ($this->product->tva_npr != $oldproduct->tva_npr) $pricemodified = true; - if ($this->product->default_vat_code != $oldproduct->default_vat_code) $pricemodified = true; + if ($result > 0 && ! empty($conf->global->PRODUCT_PRICE_UNIQ)) { + // We update price only if it was changed + $pricemodified = false; + if ($this->product->price_base_type != $oldproduct->price_base_type) { $pricemodified = true; + } else + { + if ($this->product->tva_tx != $oldproduct->tva_tx) { $pricemodified = true; + } + if ($this->product->tva_npr != $oldproduct->tva_npr) { $pricemodified = true; + } + if ($this->product->default_vat_code != $oldproduct->default_vat_code) { $pricemodified = true; + } - if ($this->product->price_base_type == 'TTC') - { - if ($this->product->price_ttc != $oldproduct->price_ttc) $pricemodified = true; - if ($this->product->price_min_ttc != $oldproduct->price_min_ttc) $pricemodified = true; - } - else - { - if ($this->product->price != $oldproduct->price) $pricemodified = true; - if ($this->product->price_min != $oldproduct->price_min) $pricemodified = true; - } - } + if ($this->product->price_base_type == 'TTC') { + if ($this->product->price_ttc != $oldproduct->price_ttc) { $pricemodified = true; + } + if ($this->product->price_min_ttc != $oldproduct->price_min_ttc) { $pricemodified = true; + } + } + else + { + if ($this->product->price != $oldproduct->price) { $pricemodified = true; + } + if ($this->product->price_min != $oldproduct->price_min) { $pricemodified = true; + } + } + } - if ($pricemodified) - { - $newvat = $this->product->tva_tx; - $newnpr = $this->product->tva_npr; - $newvatsrccode = $this->product->default_vat_code; + if ($pricemodified) { + $newvat = $this->product->tva_tx; + $newnpr = $this->product->tva_npr; + $newvatsrccode = $this->product->default_vat_code; - $newprice = $this->product->price; - $newpricemin = $this->product->price_min; - if ($this->product->price_base_type == 'TTC') - { - $newprice = $this->product->price_ttc; - $newpricemin = $this->product->price_min_ttc; - } + $newprice = $this->product->price; + $newpricemin = $this->product->price_min; + if ($this->product->price_base_type == 'TTC') { + $newprice = $this->product->price_ttc; + $newpricemin = $this->product->price_min_ttc; + } - $result = $this->product->updatePrice($newprice, $this->product->price_base_type, DolibarrApiAccess::$user, $newvat, $newpricemin, 0, $newnpr, 0, 0, array(), $newvatsrccode); - } + $result = $this->product->updatePrice($newprice, $this->product->price_base_type, DolibarrApiAccess::$user, $newvat, $newpricemin, 0, $newnpr, 0, 0, array(), $newvatsrccode); + } } - if ($result <= 0) - { - throw new RestException(500, "Error updating product", array_merge(array($this->product->error), $this->product->errors)); - } + if ($result <= 0) { + throw new RestException(500, "Error updating product", array_merge(array($this->product->error), $this->product->errors)); + } - return $this->get($id); + return $this->get($id); } /** * Delete product * - * @param int $id Product ID - * @return array + * @param int $id Product ID + * @return array */ function delete($id) { if(! DolibarrApiAccess::$user->rights->produit->supprimer) { - throw new RestException(401); - } + throw new RestException(401); + } $result = $this->product->fetch($id); - if( ! $result ) { + if(! $result ) { throw new RestException(404, 'Product not found'); } - if( ! DolibarrApi::_checkAccessToResource('product',$this->product->id)) { - throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); - } + if(! DolibarrApi::_checkAccessToResource('product', $this->product->id)) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } // The Product::delete() method uses the global variable $user. global $user; @@ -316,41 +323,41 @@ class Products extends DolibarrApi /** * Get categories for a product * - * @param int $id ID of product - * @param string $sortfield Sort field - * @param string $sortorder Sort order - * @param int $limit Limit for list - * @param int $page Page number + * @param int $id ID of product + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Limit for list + * @param int $page Page number * * @return mixed * * @url GET {id}/categories */ - function getCategories($id, $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) - { - if (! DolibarrApiAccess::$user->rights->categorie->lire) { - throw new RestException(401); - } + function getCategories($id, $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) + { + if (! DolibarrApiAccess::$user->rights->categorie->lire) { + throw new RestException(401); + } - $categories = new Categorie($this->db); + $categories = new Categorie($this->db); - $result = $categories->getListForItem($id, 'product', $sortfield, $sortorder, $limit, $page); + $result = $categories->getListForItem($id, 'product', $sortfield, $sortorder, $limit, $page); - if (empty($result)) { - throw new RestException(404, 'No category found'); - } + if (empty($result)) { + throw new RestException(404, 'No category found'); + } - if ($result < 0) { - throw new RestException(503, 'Error when retrieve category list : '.array_merge(array($categories->error), $categories->errors)); - } + if ($result < 0) { + throw new RestException(503, 'Error when retrieve category list : '.array_merge(array($categories->error), $categories->errors)); + } - return $result; + return $result; } /** * Get prices per segment for a product * - * @param int $id ID of product + * @param int $id ID of product * * @return mixed * @@ -358,41 +365,40 @@ class Products extends DolibarrApi */ function getCustomerPricesPerSegment($id) { - global $conf; + global $conf; - if (! DolibarrApiAccess::$user->rights->produit->lire) { - throw new RestException(401); - } + if (! DolibarrApiAccess::$user->rights->produit->lire) { + throw new RestException(401); + } - if (empty($conf->global->PRODUIT_MULTIPRICES)) - { - throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup'); - } + if (empty($conf->global->PRODUIT_MULTIPRICES)) { + throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup'); + } - $result = $this->product->fetch($id); - if ( ! $result ) { - throw new RestException(404, 'Product not found'); - } + $result = $this->product->fetch($id); + if (! $result ) { + throw new RestException(404, 'Product not found'); + } - if ($result < 0) { - throw new RestException(503, 'Error when retrieve prices list : '.array_merge(array($this->product->error), $this->product->errors)); - } + if ($result < 0) { + throw new RestException(503, 'Error when retrieve prices list : '.array_merge(array($this->product->error), $this->product->errors)); + } - return array( - 'multiprices'=>$this->product->multiprices, - 'multiprices_inc_tax'=>$this->product->multiprices_ttc, - 'multiprices_min'=>$this->product->multiprices_min, - 'multiprices_min_inc_tax'=>$this->product->multiprices_min_ttc, - 'multiprices_vat'=>$this->product->multiprices_tva_tx, - 'multiprices_base_type'=>$this->product->multiprices_base_type, - //'multiprices_default_vat_code'=>$this->product->multiprices_default_vat_code - ); + return array( + 'multiprices'=>$this->product->multiprices, + 'multiprices_inc_tax'=>$this->product->multiprices_ttc, + 'multiprices_min'=>$this->product->multiprices_min, + 'multiprices_min_inc_tax'=>$this->product->multiprices_min_ttc, + 'multiprices_vat'=>$this->product->multiprices_tva_tx, + 'multiprices_base_type'=>$this->product->multiprices_base_type, + //'multiprices_default_vat_code'=>$this->product->multiprices_default_vat_code + ); } /** * Get prices per customer for a product * - * @param int $id ID of product + * @param int $id ID of product * * @return mixed * @@ -400,34 +406,33 @@ class Products extends DolibarrApi */ function getCustomerPricesPerCustomer($id) { - global $conf; + global $conf; - if (! DolibarrApiAccess::$user->rights->produit->lire) { - throw new RestException(401); - } + if (! DolibarrApiAccess::$user->rights->produit->lire) { + throw new RestException(401); + } - if (empty($conf->global->PRODUIT_CUSTOMER_PRICES)) - { - throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup'); - } + if (empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { + throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup'); + } - $result = $this->product->fetch($id); - if ( ! $result ) { - throw new RestException(404, 'Product not found'); - } + $result = $this->product->fetch($id); + if (! $result ) { + throw new RestException(404, 'Product not found'); + } - if ($result < 0) { - throw new RestException(503, 'Error when retrieve prices list : '.array_merge(array($this->product->error), $this->product->errors)); - } + if ($result < 0) { + throw new RestException(503, 'Error when retrieve prices list : '.array_merge(array($this->product->error), $this->product->errors)); + } - throw new RestException(501, 'Feature not yet available'); - //return $result; + throw new RestException(501, 'Feature not yet available'); + //return $result; } /** * Get prices per quantity for a product * - * @param int $id ID of product + * @param int $id ID of product * * @return mixed * @@ -435,38 +440,37 @@ class Products extends DolibarrApi */ function getCustomerPricesPerQuantity($id) { - global $conf; + global $conf; - if (! DolibarrApiAccess::$user->rights->produit->lire) { - throw new RestException(401); - } + if (! DolibarrApiAccess::$user->rights->produit->lire) { + throw new RestException(401); + } - if (empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY)) - { - throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup'); - } + if (empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY)) { + throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup'); + } - $result = $this->product->fetch($id); - if ( ! $result ) { - throw new RestException(404, 'Product not found'); - } + $result = $this->product->fetch($id); + if (! $result ) { + throw new RestException(404, 'Product not found'); + } - if ($result < 0) { - throw new RestException(503, 'Error when retrieve prices list : '.array_merge(array($this->product->error), $this->product->errors)); - } + if ($result < 0) { + throw new RestException(503, 'Error when retrieve prices list : '.array_merge(array($this->product->error), $this->product->errors)); + } - return array( - 'prices_by_qty'=>$this->product->prices_by_qty[0], // 1 if price by quantity was activated for the product - 'prices_by_qty_list'=>$this->product->prices_by_qty_list[0] - ); + return array( + 'prices_by_qty'=>$this->product->prices_by_qty[0], // 1 if price by quantity was activated for the product + 'prices_by_qty_list'=>$this->product->prices_by_qty_list[0] + ); } /** * Clean sensible object datas * - * @param object $object Object to clean - * @return array Array of cleaned object properties + * @param object $object Object to clean + * @return array Array of cleaned object properties */ function _cleanObjectDatas($object) { @@ -492,7 +496,7 @@ class Products extends DolibarrApi /** * Validate fields before create or update object * - * @param array $data Datas to validate + * @param array $data Datas to validate * @return array * @throws RestException */ @@ -500,8 +504,9 @@ class Products extends DolibarrApi { $product = array(); foreach (Products::$FIELDS as $field) { - if (!isset($data[$field])) + if (!isset($data[$field])) { throw new RestException(400, "$field field missing"); + } $product[$field] = $data[$field]; } return $product; diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index f68464e3bc1..6d3a0724006 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2001-2007 Rodolphe Quiedeville * Copyright (C) 2004-2014 Laurent Destailleur * Copyright (C) 2005-2015 Regis Houssin * Copyright (C) 2006 Andre Cianfarani @@ -30,9 +30,9 @@ */ /** - * \file htdocs/product/class/product.class.php - * \ingroup produit - * \brief File of class to manage predefined products or services + * \file htdocs/product/class/product.class.php + * \ingroup produit + * \brief File of class to manage predefined products or services */ require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php'; @@ -43,628 +43,646 @@ require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; */ class Product extends CommonObject { - /** - * @var string ID to identify managed object - */ - public $element='product'; + /** + * @var string ID to identify managed object + */ + public $element='product'; - /** - * @var string Name of table without prefix where object is stored - */ - public $table_element='product'; + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element='product'; - /** - * @var int Field with ID of parent key if this field has a parent - */ - public $fk_element='fk_product'; + /** + * @var int Field with ID of parent key if this field has a parent + */ + public $fk_element='fk_product'; - protected $childtables=array('supplier_proposaldet', 'propaldet','commandedet','facturedet','contratdet','facture_fourn_det','commande_fournisseurdet'); // To test if we can delete object + protected $childtables=array('supplier_proposaldet', 'propaldet','commandedet','facturedet','contratdet','facture_fourn_det','commande_fournisseurdet'); // To test if we can delete object - /** - * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe - * @var int - */ - public $ismultientitymanaged = 1; + /** + * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + * + * @var int + */ + public $ismultientitymanaged = 1; - /** - * {@inheritdoc} - */ - protected $table_ref_field = 'ref'; + /** + * {@inheritdoc} + */ + protected $table_ref_field = 'ref'; - public $regeximgext='\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.xpm|\.xbm'; // See also into images.lib.php + public $regeximgext='\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.xpm|\.xbm'; // See also into images.lib.php - /* - * @deprecated - * @see label - */ - public $libelle; - /** - * Product label - * @var string - */ - public $label; + /* + * @deprecated + * @see label + */ + public $libelle; + /** + * Product label + * + * @var string + */ + public $label; - /** - * Product descripion - * @var string - */ - public $description; + /** + * Product descripion + * + * @var string + */ + public $description; - /** - * Check TYPE constants - * @var int - */ - public $type = self::TYPE_PRODUCT; + /** + * Check TYPE constants + * + * @var int + */ + public $type = self::TYPE_PRODUCT; - /** - * Selling price - * @var float - */ - public $price; // Price net + /** + * Selling price + * + * @var float + */ + public $price; // Price net - /** - * Price with tax - * @var float - */ - public $price_ttc; + /** + * Price with tax + * + * @var float + */ + public $price_ttc; - /** - * Minimum price net - * @var float - */ - public $price_min; + /** + * Minimum price net + * + * @var float + */ + public $price_min; - /** - * Minimum price with tax - * @var float - */ - public $price_min_ttc; + /** + * Minimum price with tax + * + * @var float + */ + public $price_min_ttc; - /* - * Base price ('TTC' for price including tax or 'HT' for net price) - * @var float - */ - public $price_base_type; + /* + * Base price ('TTC' for price including tax or 'HT' for net price) + * @var float + */ + public $price_base_type; - //! Arrays for multiprices - public $multiprices=array(); - public $multiprices_ttc=array(); - public $multiprices_base_type=array(); - public $multiprices_min=array(); - public $multiprices_min_ttc=array(); - public $multiprices_tva_tx=array(); - public $multiprices_recuperableonly=array(); + //! Arrays for multiprices + public $multiprices=array(); + public $multiprices_ttc=array(); + public $multiprices_base_type=array(); + public $multiprices_min=array(); + public $multiprices_min_ttc=array(); + public $multiprices_tva_tx=array(); + public $multiprices_recuperableonly=array(); - //! Price by quantity arrays - public $price_by_qty; - public $prices_by_qty=array(); - public $prices_by_qty_id=array(); - public $prices_by_qty_list=array(); + //! Price by quantity arrays + public $price_by_qty; + public $prices_by_qty=array(); + public $prices_by_qty_id=array(); + public $prices_by_qty_list=array(); - //! Default VAT code for product (link to code into llx_c_tva but without foreign keys) - public $default_vat_code; + //! Default VAT code for product (link to code into llx_c_tva but without foreign keys) + public $default_vat_code; - //! Default VAT rate of product - public $tva_tx; + //! Default VAT rate of product + public $tva_tx; - //! French VAT NPR (0 or 1) + //! French VAT NPR (0 or 1) public $tva_npr=0; - //! Other local taxes - public $localtax1_tx; - public $localtax2_tx; - public $localtax1_type; - public $localtax2_type; + //! Other local taxes + public $localtax1_tx; + public $localtax2_tx; + public $localtax1_type; + public $localtax2_type; - /** - * Stock real - * @var int - */ - public $stock_reel = 0; + /** + * Stock real + * + * @var int + */ + public $stock_reel = 0; - /** - * Stock virtual - * @var int - */ - public $stock_theorique; + /** + * Stock virtual + * + * @var int + */ + public $stock_theorique; - /** - * Cost price - * @var float - */ - public $cost_price; + /** + * Cost price + * + * @var float + */ + public $cost_price; - //! Average price value for product entry into stock (PMP) - public $pmp; + //! Average price value for product entry into stock (PMP) + public $pmp; - /** - * Stock alert - * @var int - */ - public $seuil_stock_alerte=0; + /** + * Stock alert + * + * @var int + */ + public $seuil_stock_alerte=0; - /** - * Ask for replenishment when $desiredstock < $stock_reel - */ - public $desiredstock=0; + /** + * Ask for replenishment when $desiredstock < $stock_reel + */ + public $desiredstock=0; - /* - * Service expiration - */ - public $duration_value; + /* + * Service expiration + */ + public $duration_value; - /** - * Exoiration unit - */ - public $duration_unit; + /** + * Exoiration unit + */ + public $duration_unit; - /** - * Status indicates whether the product is on sale '1' or not '0' - * @var int - */ - public $status=0; + /** + * Status indicates whether the product is on sale '1' or not '0' + * + * @var int + */ + public $status=0; - /** - * Status indicate whether the product is available for purchase '1' or not '0' - * @var int - */ - public $status_buy=0; + /** + * Status indicate whether the product is available for purchase '1' or not '0' + * + * @var int + */ + public $status_buy=0; - /** - * Status indicates whether the product is a finished product '1' or a raw material '0' - * @var int - */ - public $finished; + /** + * Status indicates whether the product is a finished product '1' or a raw material '0' + * + * @var int + */ + public $finished; - /** - * We must manage lot/batch number, sell-by date and so on : '1':yes '0':no - * @var int - */ - public $status_batch=0; + /** + * We must manage lot/batch number, sell-by date and so on : '1':yes '0':no + * + * @var int + */ + public $status_batch=0; - /** - * Customs code - * @var - */ - public $customcode; + /** + * Customs code + * + * @var + */ + public $customcode; - /** - * Product URL - * @var string - */ - public $url; + /** + * Product URL + * + * @var string + */ + public $url; - //! Unites de mesure - public $weight; - public $weight_units; - public $length; - public $length_units; - public $surface; - public $surface_units; - public $volume; - public $volume_units; + //! Unites de mesure + public $weight; + public $weight_units; + public $length; + public $length_units; + public $surface; + public $surface_units; + public $volume; + public $volume_units; - public $accountancy_code_sell; - public $accountancy_code_sell_intra; - public $accountancy_code_sell_export; - public $accountancy_code_buy; + public $accountancy_code_sell; + public $accountancy_code_sell_intra; + public $accountancy_code_sell_export; + public $accountancy_code_buy; - /** - * Main barcode - * barcode value - * @var - */ - public $barcode; + /** + * Main barcode + * barcode value + * + * @var + */ + public $barcode; - /** - * Additional barcodes (Some products have different barcodes according to the country of origin of manufacture) - * @var array - */ - public $barcodes_extra=array(); + /** + * Additional barcodes (Some products have different barcodes according to the country of origin of manufacture) + * + * @var array + */ + public $barcodes_extra=array(); - public $stats_propale=array(); - public $stats_commande=array(); - public $stats_contrat=array(); - public $stats_facture=array(); + public $stats_propale=array(); + public $stats_commande=array(); + public $stats_contrat=array(); + public $stats_facture=array(); public $stats_commande_fournisseur=array(); - public $multilangs=array(); + public $multilangs=array(); - //! Taille de l'image - public $imgWidth; - public $imgHeight; + //! Taille de l'image + public $imgWidth; + public $imgHeight; - public $date_creation; - public $date_modification; + public $date_creation; + public $date_modification; - //! Id du fournisseur - public $product_fourn_id; + //! Id du fournisseur + public $product_fourn_id; - //! Product ID already linked to a reference supplier - public $product_id_already_linked; + //! Product ID already linked to a reference supplier + public $product_id_already_linked; - public $nbphoto=0; + public $nbphoto=0; - //! Contains detail of stock of product into each warehouse - public $stock_warehouse=array(); + //! Contains detail of stock of product into each warehouse + public $stock_warehouse=array(); - public $oldcopy; + public $oldcopy; - /** + /** * @var int ID */ public $fk_price_expression; /* To store supplier price found */ - public $fourn_pu; - public $fourn_price_base_type; - public $fourn_socid; + public $fourn_pu; + public $fourn_price_base_type; + public $fourn_socid; - /** - * @deprecated - * @see $ref_supplier - */ - public $ref_fourn; - public $ref_supplier; + /** + * @deprecated + * @see $ref_supplier + */ + public $ref_fourn; + public $ref_supplier; - /** - * Unit code ('km', 'm', 'l', 'p', ...) - * @var string - */ - public $fk_unit; + /** + * Unit code ('km', 'm', 'l', 'p', ...) + * + * @var string + */ + public $fk_unit; - /** - * Price is generated using multiprice rules - * @var int - */ - public $price_autogen = 0; + /** + * Price is generated using multiprice rules + * + * @var int + */ + public $price_autogen = 0; - public $fields = array( - 'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'index'=>1, 'position'=>1, 'comment'=>'Id'), - 'ref' =>array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>10, 'searchall'=>1, 'comment'=>'Reference of object'), - 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'default'=>1, 'notnull'=>1, 'index'=>1, 'position'=>20), - 'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>61), - 'note' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>62), - 'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>500), - 'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>501), - //'date_valid' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>502), - 'fk_user_author'=>array('type'=>'integer', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>510, 'foreignkey'=>'llx_user.rowid'), - 'fk_user_modif' =>array('type'=>'integer', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>511), - //'fk_user_valid' =>array('type'=>'integer', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>512), - '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')), - ); + public $fields = array( + 'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'index'=>1, 'position'=>1, 'comment'=>'Id'), + 'ref' =>array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>10, 'searchall'=>1, 'comment'=>'Reference of object'), + 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'default'=>1, 'notnull'=>1, 'index'=>1, 'position'=>20), + 'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>61), + 'note' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>62), + 'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>500), + 'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>501), + //'date_valid' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>502), + 'fk_user_author'=>array('type'=>'integer', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>510, 'foreignkey'=>'llx_user.rowid'), + 'fk_user_modif' =>array('type'=>'integer', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>511), + //'fk_user_valid' =>array('type'=>'integer', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>512), + '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')), + ); - /** - * Regular product - */ - const TYPE_PRODUCT = 0; - /** - * Service - */ - const TYPE_SERVICE = 1; - /** - * Advanced feature: assembly kit - */ - const TYPE_ASSEMBLYKIT = 2; - /** - * Advanced feature: stock kit - */ - const TYPE_STOCKKIT = 3; + /** + * Regular product + */ + const TYPE_PRODUCT = 0; + /** + * Service + */ + const TYPE_SERVICE = 1; + /** + * Advanced feature: assembly kit + */ + const TYPE_ASSEMBLYKIT = 2; + /** + * Advanced feature: stock kit + */ + const TYPE_STOCKKIT = 3; - /** - * Constructor - * - * @param DoliDB $db Database handler - */ - function __construct($db) - { - $this->db = $db; - $this->canvas = ''; - } + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + function __construct($db) + { + $this->db = $db; + $this->canvas = ''; + } - /** - * Check that ref and label are ok - * - * @return int >1 if OK, <=0 if KO - */ - function check() - { - $this->ref = dol_sanitizeFileName(stripslashes($this->ref)); + /** + * Check that ref and label are ok + * + * @return int >1 if OK, <=0 if KO + */ + function check() + { + $this->ref = dol_sanitizeFileName(stripslashes($this->ref)); - $err = 0; - if (dol_strlen(trim($this->ref)) == 0) - $err++; + $err = 0; + if (dol_strlen(trim($this->ref)) == 0) { + $err++; + } - if (dol_strlen(trim($this->label)) == 0) - $err++; + if (dol_strlen(trim($this->label)) == 0) { + $err++; + } - if ($err > 0) - { - return 0; - } - else - { - return 1; - } - } - - /** - * Insert product into database - * - * @param User $user User making insert - * @param int $notrigger Disable triggers - * @return int Id of product/service if OK, < 0 if KO - */ - function create($user,$notrigger=0) - { - global $conf, $langs; - - $error=0; - - // Clean parameters - $this->ref = dol_string_nospecial(trim($this->ref)); - $this->label = trim($this->label); - $this->price_ttc=price2num($this->price_ttc); - $this->price=price2num($this->price); - $this->price_min_ttc=price2num($this->price_min_ttc); - $this->price_min=price2num($this->price_min); - if (empty($this->tva_tx)) $this->tva_tx = 0; - if (empty($this->tva_npr)) $this->tva_npr = 0; - //Local taxes - if (empty($this->localtax1_tx)) $this->localtax1_tx = 0; - if (empty($this->localtax2_tx)) $this->localtax2_tx = 0; - if (empty($this->localtax1_type)) $this->localtax1_type = '0'; - if (empty($this->localtax2_type)) $this->localtax2_type = '0'; - - if (empty($this->price)) $this->price = 0; - if (empty($this->price_min)) $this->price_min = 0; - - // Price by quantity - if (empty($this->price_by_qty)) $this->price_by_qty = 0; - - if (empty($this->status)) $this->status = 0; - if (empty($this->status_buy)) $this->status_buy = 0; - - $price_ht=0; - $price_ttc=0; - $price_min_ht=0; - $price_min_ttc=0; - - // - if ($this->price_base_type == 'TTC' && $this->price_ttc > 0) - { - $price_ttc = price2num($this->price_ttc,'MU'); - $price_ht = price2num($this->price_ttc / (1 + ($this->tva_tx / 100)),'MU'); - } - - // - if ($this->price_base_type != 'TTC' && $this->price > 0) - { - $price_ht = price2num($this->price,'MU'); - $price_ttc = price2num($this->price * (1 + ($this->tva_tx / 100)),'MU'); - } - - // - if (($this->price_min_ttc > 0) && ($this->price_base_type == 'TTC')) - { - $price_min_ttc = price2num($this->price_min_ttc,'MU'); - $price_min_ht = price2num($this->price_min_ttc / (1 + ($this->tva_tx / 100)),'MU'); - } - - // - if (($this->price_min > 0) && ($this->price_base_type != 'TTC')) - { - $price_min_ht = price2num($this->price_min,'MU'); - $price_min_ttc = price2num($this->price_min * (1 + ($this->tva_tx / 100)),'MU'); - } - - $this->accountancy_code_buy = trim($this->accountancy_code_buy); - $this->accountancy_code_sell= trim($this->accountancy_code_sell); - $this->accountancy_code_sell_intra= trim($this->accountancy_code_sell_intra); - $this->accountancy_code_sell_export= trim($this->accountancy_code_sell_export); - - // Barcode value - $this->barcode=trim($this->barcode); - - // Check parameters - if (empty($this->label)) - { - $this->error='ErrorMandatoryParametersNotProvided'; - return -1; - } - - if (empty($this->ref)) - { - // Load object modCodeProduct - $module=(! empty($conf->global->PRODUCT_CODEPRODUCT_ADDON)?$conf->global->PRODUCT_CODEPRODUCT_ADDON:'mod_codeproduct_leopard'); - if ($module != 'mod_codeproduct_leopard') // Do not load module file for leopard - { - if (substr($module, 0, 16) == 'mod_codeproduct_' && substr($module, -3) == 'php') - { - $module = substr($module, 0, dol_strlen($module)-4); - } - dol_include_once('/core/modules/product/'.$module.'.php'); - $modCodeProduct = new $module; - if (! empty($modCodeProduct->code_auto)) - { - $this->ref = $modCodeProduct->getNextValue($this,$this->type); - } - unset($modCodeProduct); - } - - if (empty($this->ref)) - { - $this->error='ProductModuleNotSetupForAutoRef'; - return -2; - } - } - - dol_syslog(get_class($this)."::create ref=".$this->ref." price=".$this->price." price_ttc=".$this->price_ttc." tva_tx=".$this->tva_tx." price_base_type=".$this->price_base_type, LOG_DEBUG); - - $now=dol_now(); - - $this->db->begin(); - - // For automatic creation during create action (not used by Dolibarr GUI, can be used by scripts) - if ($this->barcode == -1) $this->barcode = $this->get_barcode($this,$this->barcode_type_code); - - // Check more parameters - // If error, this->errors[] is filled - $result = $this->verify(); - - if ($result >= 0) - { - $sql = "SELECT count(*) as nb"; - $sql.= " FROM ".MAIN_DB_PREFIX."product"; - $sql.= " WHERE entity IN (".getEntity('product').")"; - $sql.= " AND ref = '" .$this->db->escape($this->ref)."'"; - - $result = $this->db->query($sql); - if ($result) - { - $obj = $this->db->fetch_object($result); - if ($obj->nb == 0) - { - // Produit non deja existant - $sql = "INSERT INTO ".MAIN_DB_PREFIX."product ("; - $sql.= "datec"; - $sql.= ", entity"; - $sql.= ", ref"; - $sql.= ", ref_ext"; - $sql.= ", price_min"; - $sql.= ", price_min_ttc"; - $sql.= ", label"; - $sql.= ", fk_user_author"; - $sql.= ", fk_product_type"; - $sql.= ", price"; - $sql.= ", price_ttc"; - $sql.= ", price_base_type"; - $sql.= ", tobuy"; - $sql.= ", tosell"; - $sql.= ", accountancy_code_buy"; - $sql.= ", accountancy_code_sell"; - $sql.= ", accountancy_code_sell_intra"; - $sql.= ", accountancy_code_sell_export"; - $sql.= ", canvas"; - $sql.= ", finished"; - $sql.= ", tobatch"; - $sql.= ", fk_unit"; - $sql.= ") VALUES ("; - $sql.= "'".$this->db->idate($now)."'"; - $sql.= ", ".$conf->entity; - $sql.= ", '".$this->db->escape($this->ref)."'"; - $sql.= ", ".(! empty($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null"); - $sql.= ", ".price2num($price_min_ht); - $sql.= ", ".price2num($price_min_ttc); - $sql.= ", ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null"); - $sql.= ", ".$user->id; - $sql.= ", ".$this->type; - $sql.= ", ".price2num($price_ht); - $sql.= ", ".price2num($price_ttc); - $sql.= ", '".$this->db->escape($this->price_base_type)."'"; - $sql.= ", ".$this->status; - $sql.= ", ".$this->status_buy; - $sql.= ", '".$this->db->escape($this->accountancy_code_buy)."'"; - $sql.= ", '".$this->db->escape($this->accountancy_code_sell)."'"; - $sql.= ", '".$this->db->escape($this->accountancy_code_sell_intra)."'"; - $sql.= ", '".$this->db->escape($this->accountancy_code_sell_export)."'"; - $sql.= ", '".$this->db->escape($this->canvas)."'"; - $sql.= ", ".((! isset($this->finished) || $this->finished < 0 || $this->finished == '') ? 'null' : (int) $this->finished); - $sql.= ", ".((empty($this->status_batch) || $this->status_batch < 0)? '0':$this->status_batch); - $sql.= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit); - $sql.= ")"; - - dol_syslog(get_class($this)."::Create", LOG_DEBUG); - $result = $this->db->query($sql); - if ( $result ) - { - $id = $this->db->last_insert_id(MAIN_DB_PREFIX."product"); - - if ($id > 0) - { - $this->id = $id; - $this->price = $price_ht; - $this->price_ttc = $price_ttc; - $this->price_min = $price_min_ht; - $this->price_min_ttc = $price_min_ttc; - - $result = $this->_log_price($user); - if ($result > 0) - { - if ($this->update($id, $user, true, 'add') <= 0) - { - $error++; - } - } - else - { - $error++; - $this->error=$this->db->lasterror(); - } - } - else - { - $error++; - $this->error='ErrorFailedToGetInsertedId'; - } - } - else - { - $error++; - $this->error=$this->db->lasterror(); - } - } - else - { - // Product already exists with this ref - $langs->load("products"); - $error++; - $this->error = "ErrorProductAlreadyExists"; - } - } - else - { - $error++; - $this->error=$this->db->lasterror(); - } - - if (! $error && ! $notrigger) - { - // Call trigger - $result=$this->call_trigger('PRODUCT_CREATE',$user); - if ($result < 0) { $error++; } - // End call triggers - } - - if (! $error) - { - $this->db->commit(); - return $this->id; - } - else - { - $this->db->rollback(); - return -$error; - } + if ($err > 0) { + return 0; } else - { + { + return 1; + } + } + + /** + * Insert product into database + * + * @param User $user User making insert + * @param int $notrigger Disable triggers + * @return int Id of product/service if OK, < 0 if KO + */ + function create($user,$notrigger=0) + { + global $conf, $langs; + + $error=0; + + // Clean parameters + $this->ref = dol_string_nospecial(trim($this->ref)); + $this->label = trim($this->label); + $this->price_ttc=price2num($this->price_ttc); + $this->price=price2num($this->price); + $this->price_min_ttc=price2num($this->price_min_ttc); + $this->price_min=price2num($this->price_min); + if (empty($this->tva_tx)) { $this->tva_tx = 0; + } + if (empty($this->tva_npr)) { $this->tva_npr = 0; + } + //Local taxes + if (empty($this->localtax1_tx)) { $this->localtax1_tx = 0; + } + if (empty($this->localtax2_tx)) { $this->localtax2_tx = 0; + } + if (empty($this->localtax1_type)) { $this->localtax1_type = '0'; + } + if (empty($this->localtax2_type)) { $this->localtax2_type = '0'; + } + + if (empty($this->price)) { $this->price = 0; + } + if (empty($this->price_min)) { $this->price_min = 0; + } + + // Price by quantity + if (empty($this->price_by_qty)) { $this->price_by_qty = 0; + } + + if (empty($this->status)) { $this->status = 0; + } + if (empty($this->status_buy)) { $this->status_buy = 0; + } + + $price_ht=0; + $price_ttc=0; + $price_min_ht=0; + $price_min_ttc=0; + + // + if ($this->price_base_type == 'TTC' && $this->price_ttc > 0) { + $price_ttc = price2num($this->price_ttc, 'MU'); + $price_ht = price2num($this->price_ttc / (1 + ($this->tva_tx / 100)), 'MU'); + } + + // + if ($this->price_base_type != 'TTC' && $this->price > 0) { + $price_ht = price2num($this->price, 'MU'); + $price_ttc = price2num($this->price * (1 + ($this->tva_tx / 100)), 'MU'); + } + + // + if (($this->price_min_ttc > 0) && ($this->price_base_type == 'TTC')) { + $price_min_ttc = price2num($this->price_min_ttc, 'MU'); + $price_min_ht = price2num($this->price_min_ttc / (1 + ($this->tva_tx / 100)), 'MU'); + } + + // + if (($this->price_min > 0) && ($this->price_base_type != 'TTC')) { + $price_min_ht = price2num($this->price_min, 'MU'); + $price_min_ttc = price2num($this->price_min * (1 + ($this->tva_tx / 100)), 'MU'); + } + + $this->accountancy_code_buy = trim($this->accountancy_code_buy); + $this->accountancy_code_sell= trim($this->accountancy_code_sell); + $this->accountancy_code_sell_intra= trim($this->accountancy_code_sell_intra); + $this->accountancy_code_sell_export= trim($this->accountancy_code_sell_export); + + // Barcode value + $this->barcode=trim($this->barcode); + + // Check parameters + if (empty($this->label)) { + $this->error='ErrorMandatoryParametersNotProvided'; + return -1; + } + + if (empty($this->ref)) { + // Load object modCodeProduct + $module=(! empty($conf->global->PRODUCT_CODEPRODUCT_ADDON)?$conf->global->PRODUCT_CODEPRODUCT_ADDON:'mod_codeproduct_leopard'); + if ($module != 'mod_codeproduct_leopard') // Do not load module file for leopard + { + if (substr($module, 0, 16) == 'mod_codeproduct_' && substr($module, -3) == 'php') { + $module = substr($module, 0, dol_strlen($module)-4); + } + dol_include_once('/core/modules/product/'.$module.'.php'); + $modCodeProduct = new $module; + if (! empty($modCodeProduct->code_auto)) { + $this->ref = $modCodeProduct->getNextValue($this, $this->type); + } + unset($modCodeProduct); + } + + if (empty($this->ref)) { + $this->error='ProductModuleNotSetupForAutoRef'; + return -2; + } + } + + dol_syslog(get_class($this)."::create ref=".$this->ref." price=".$this->price." price_ttc=".$this->price_ttc." tva_tx=".$this->tva_tx." price_base_type=".$this->price_base_type, LOG_DEBUG); + + $now=dol_now(); + + $this->db->begin(); + + // For automatic creation during create action (not used by Dolibarr GUI, can be used by scripts) + if ($this->barcode == -1) { $this->barcode = $this->get_barcode($this, $this->barcode_type_code); + } + + // Check more parameters + // If error, this->errors[] is filled + $result = $this->verify(); + + if ($result >= 0) { + $sql = "SELECT count(*) as nb"; + $sql.= " FROM ".MAIN_DB_PREFIX."product"; + $sql.= " WHERE entity IN (".getEntity('product').")"; + $sql.= " AND ref = '" .$this->db->escape($this->ref)."'"; + + $result = $this->db->query($sql); + if ($result) { + $obj = $this->db->fetch_object($result); + if ($obj->nb == 0) { + // Produit non deja existant + $sql = "INSERT INTO ".MAIN_DB_PREFIX."product ("; + $sql.= "datec"; + $sql.= ", entity"; + $sql.= ", ref"; + $sql.= ", ref_ext"; + $sql.= ", price_min"; + $sql.= ", price_min_ttc"; + $sql.= ", label"; + $sql.= ", fk_user_author"; + $sql.= ", fk_product_type"; + $sql.= ", price"; + $sql.= ", price_ttc"; + $sql.= ", price_base_type"; + $sql.= ", tobuy"; + $sql.= ", tosell"; + $sql.= ", accountancy_code_buy"; + $sql.= ", accountancy_code_sell"; + $sql.= ", accountancy_code_sell_intra"; + $sql.= ", accountancy_code_sell_export"; + $sql.= ", canvas"; + $sql.= ", finished"; + $sql.= ", tobatch"; + $sql.= ", fk_unit"; + $sql.= ") VALUES ("; + $sql.= "'".$this->db->idate($now)."'"; + $sql.= ", ".$conf->entity; + $sql.= ", '".$this->db->escape($this->ref)."'"; + $sql.= ", ".(! empty($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null"); + $sql.= ", ".price2num($price_min_ht); + $sql.= ", ".price2num($price_min_ttc); + $sql.= ", ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null"); + $sql.= ", ".$user->id; + $sql.= ", ".$this->type; + $sql.= ", ".price2num($price_ht); + $sql.= ", ".price2num($price_ttc); + $sql.= ", '".$this->db->escape($this->price_base_type)."'"; + $sql.= ", ".$this->status; + $sql.= ", ".$this->status_buy; + $sql.= ", '".$this->db->escape($this->accountancy_code_buy)."'"; + $sql.= ", '".$this->db->escape($this->accountancy_code_sell)."'"; + $sql.= ", '".$this->db->escape($this->accountancy_code_sell_intra)."'"; + $sql.= ", '".$this->db->escape($this->accountancy_code_sell_export)."'"; + $sql.= ", '".$this->db->escape($this->canvas)."'"; + $sql.= ", ".((! isset($this->finished) || $this->finished < 0 || $this->finished == '') ? 'null' : (int) $this->finished); + $sql.= ", ".((empty($this->status_batch) || $this->status_batch < 0)? '0':$this->status_batch); + $sql.= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit); + $sql.= ")"; + + dol_syslog(get_class($this)."::Create", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result ) { + $id = $this->db->last_insert_id(MAIN_DB_PREFIX."product"); + + if ($id > 0) { + $this->id = $id; + $this->price = $price_ht; + $this->price_ttc = $price_ttc; + $this->price_min = $price_min_ht; + $this->price_min_ttc = $price_min_ttc; + + $result = $this->_log_price($user); + if ($result > 0) { + if ($this->update($id, $user, true, 'add') <= 0) { + $error++; + } + } + else + { + $error++; + $this->error=$this->db->lasterror(); + } + } + else + { + $error++; + $this->error='ErrorFailedToGetInsertedId'; + } + } + else + { + $error++; + $this->error=$this->db->lasterror(); + } + } + else + { + // Product already exists with this ref + $langs->load("products"); + $error++; + $this->error = "ErrorProductAlreadyExists"; + } + } + else + { + $error++; + $this->error=$this->db->lasterror(); + } + + if (! $error && ! $notrigger) { + // Call trigger + $result=$this->call_trigger('PRODUCT_CREATE', $user); + if ($result < 0) { $error++; + } + // End call triggers + } + + if (! $error) { + $this->db->commit(); + return $this->id; + } + else + { + $this->db->rollback(); + return -$error; + } + } + else + { $this->db->rollback(); - dol_syslog(get_class($this)."::Create fails verify ".join(',',$this->errors), LOG_WARNING); + dol_syslog(get_class($this)."::Create fails verify ".join(',', $this->errors), LOG_WARNING); return -3; } - } + } /** * Check properties of product are ok (like name, barcode, ...). * All properties must be already loaded on object (this->barcode, this->barcode_type_code, ...). * - * @return int 0 if OK, <0 if KO + * @return int 0 if OK, <0 if KO */ function verify() { @@ -673,30 +691,25 @@ class Product extends CommonObject $result = 0; $this->ref = trim($this->ref); - if (! $this->ref) - { + if (! $this->ref) { $this->errors[] = 'ErrorBadRef'; $result = -2; } $rescode = $this->check_barcode($this->barcode, $this->barcode_type_code); - if ($rescode) - { - if ($rescode == -1) - { - $this->errors[] = 'ErrorBadBarCodeSyntax'; - } - elseif ($rescode == -2) - { - $this->errors[] = 'ErrorBarCodeRequired'; - } - elseif ($rescode == -3) - { - // Note: Common usage is to have barcode unique. For variants, we should have a different barcode. - $this->errors[] = 'ErrorBarCodeAlreadyUsed'; - } + if ($rescode) { + if ($rescode == -1) { + $this->errors[] = 'ErrorBadBarCodeSyntax'; + } + elseif ($rescode == -2) { + $this->errors[] = 'ErrorBarCodeRequired'; + } + elseif ($rescode == -3) { + // Note: Common usage is to have barcode unique. For variants, we should have a different barcode. + $this->errors[] = 'ErrorBarCodeAlreadyUsed'; + } - $result = -3; + $result = -3; } return $result; @@ -706,26 +719,26 @@ class Product extends CommonObject /** * Check barcode * - * @param string $valuetotest Value to test - * @param string $typefortest Type of barcode (ISBN, EAN, ...) - * @return int 0 if OK - * -1 ErrorBadBarCodeSyntax - * -2 ErrorBarCodeRequired - * -3 ErrorBarCodeAlreadyUsed + * @param string $valuetotest Value to test + * @param string $typefortest Type of barcode (ISBN, EAN, ...) + * @return int 0 if OK + * -1 ErrorBadBarCodeSyntax + * -2 ErrorBarCodeRequired + * -3 ErrorBarCodeAlreadyUsed */ function check_barcode($valuetotest,$typefortest) { // phpcs:enable global $conf; - if (! empty($conf->barcode->enabled) && ! empty($conf->global->BARCODE_PRODUCT_ADDON_NUM)) - { - $module=strtolower($conf->global->BARCODE_PRODUCT_ADDON_NUM); + if (! empty($conf->barcode->enabled) && ! empty($conf->global->BARCODE_PRODUCT_ADDON_NUM)) { + $module=strtolower($conf->global->BARCODE_PRODUCT_ADDON_NUM); - $dirsociete=array_merge(array('/core/modules/barcode/'),$conf->modules_parts['barcode']); + $dirsociete=array_merge(array('/core/modules/barcode/'), $conf->modules_parts['barcode']); foreach ($dirsociete as $dirroot) { $res=dol_include_once($dirroot.$module.'.php'); - if ($res) break; + if ($res) { break; + } } $mod = new $module(); @@ -735,95 +748,102 @@ class Product extends CommonObject return $result; } else - { + { return 0; } } - /** - * Update a record into database. - * If batch flag is set to on, we create records into llx_product_batch - * - * @param int $id Id of product - * @param User $user Object user making update - * @param int $notrigger Disable triggers - * @param string $action Current action for hookmanager ('add' or 'update') - * @return int 1 if OK, -1 if ref already exists, -2 if other error - */ - function update($id, $user, $notrigger=false, $action='update') - { - global $langs, $conf, $hookmanager; + /** + * Update a record into database. + * If batch flag is set to on, we create records into llx_product_batch + * + * @param int $id Id of product + * @param User $user Object user making update + * @param int $notrigger Disable triggers + * @param string $action Current action for hookmanager ('add' or 'update') + * @return int 1 if OK, -1 if ref already exists, -2 if other error + */ + function update($id, $user, $notrigger=false, $action='update') + { + global $langs, $conf, $hookmanager; - $error=0; + $error=0; - // Check parameters - if (! $this->label) $this->label = 'MISSING LABEL'; + // Check parameters + if (! $this->label) { $this->label = 'MISSING LABEL'; + } - // Clean parameters - $this->ref = dol_string_nospecial(trim($this->ref)); - $this->label = trim($this->label); - $this->description = trim($this->description); - $this->note = (isset($this->note) ? trim($this->note) : null); - $this->weight = price2num($this->weight); - $this->weight_units = trim($this->weight_units); - $this->length = price2num($this->length); - $this->length_units = trim($this->length_units); - $this->width = price2num($this->width); - $this->width_units = trim($this->width_units); - $this->height = price2num($this->height); - $this->height_units = trim($this->height_units); - // set unit not defined - if ($this->length_units) $this->width_units = $this->length_units; // Not used yet - if ($this->length_units) $this->height_units = $this->length_units; // Not used yet - // Automated compute surface and volume if not filled - if (empty($this->surface) && !empty($this->length) && !empty($this->width) && $this->length_units == $this->width_units) - { - $this->surface = $this->length * $this->width; - $this->surface_units = measuring_units_squared($this->length_units); - } - if (empty($this->volume) && !empty($this->surface_units) && !empty($this->height) && $this->length_units == $this->height_units) - { - $this->volume = $this->surface * $this->height; - $this->volume_units = measuring_units_cubed($this->height_units); - } + // Clean parameters + $this->ref = dol_string_nospecial(trim($this->ref)); + $this->label = trim($this->label); + $this->description = trim($this->description); + $this->note = (isset($this->note) ? trim($this->note) : null); + $this->weight = price2num($this->weight); + $this->weight_units = trim($this->weight_units); + $this->length = price2num($this->length); + $this->length_units = trim($this->length_units); + $this->width = price2num($this->width); + $this->width_units = trim($this->width_units); + $this->height = price2num($this->height); + $this->height_units = trim($this->height_units); + // set unit not defined + if ($this->length_units) { $this->width_units = $this->length_units; // Not used yet + } + if ($this->length_units) { $this->height_units = $this->length_units; // Not used yet + } + // Automated compute surface and volume if not filled + if (empty($this->surface) && !empty($this->length) && !empty($this->width) && $this->length_units == $this->width_units) { + $this->surface = $this->length * $this->width; + $this->surface_units = measuring_units_squared($this->length_units); + } + if (empty($this->volume) && !empty($this->surface_units) && !empty($this->height) && $this->length_units == $this->height_units) { + $this->volume = $this->surface * $this->height; + $this->volume_units = measuring_units_cubed($this->height_units); + } - $this->surface = price2num($this->surface); - $this->surface_units = trim($this->surface_units); - $this->volume = price2num($this->volume); - $this->volume_units = trim($this->volume_units); - if (empty($this->tva_tx)) $this->tva_tx = 0; - if (empty($this->tva_npr)) $this->tva_npr = 0; - if (empty($this->localtax1_tx)) $this->localtax1_tx = 0; - if (empty($this->localtax2_tx)) $this->localtax2_tx = 0; - if (empty($this->localtax1_type)) $this->localtax1_type = '0'; - if (empty($this->localtax2_type)) $this->localtax2_type = '0'; - if (empty($this->status)) $this->status = 0; - if (empty($this->status_buy)) $this->status_buy = 0; + $this->surface = price2num($this->surface); + $this->surface_units = trim($this->surface_units); + $this->volume = price2num($this->volume); + $this->volume_units = trim($this->volume_units); + if (empty($this->tva_tx)) { $this->tva_tx = 0; + } + if (empty($this->tva_npr)) { $this->tva_npr = 0; + } + if (empty($this->localtax1_tx)) { $this->localtax1_tx = 0; + } + if (empty($this->localtax2_tx)) { $this->localtax2_tx = 0; + } + if (empty($this->localtax1_type)) { $this->localtax1_type = '0'; + } + if (empty($this->localtax2_type)) { $this->localtax2_type = '0'; + } + if (empty($this->status)) { $this->status = 0; + } + if (empty($this->status_buy)) { $this->status_buy = 0; + } - if (empty($this->country_id)) $this->country_id = 0; + if (empty($this->country_id)) { $this->country_id = 0; + } // Barcode value $this->barcode=trim($this->barcode); - $this->accountancy_code_buy = trim($this->accountancy_code_buy); - $this->accountancy_code_sell= trim($this->accountancy_code_sell); - $this->accountancy_code_sell_intra= trim($this->accountancy_code_sell_intra); - $this->accountancy_code_sell_export= trim($this->accountancy_code_sell_export); + $this->accountancy_code_buy = trim($this->accountancy_code_buy); + $this->accountancy_code_sell= trim($this->accountancy_code_sell); + $this->accountancy_code_sell_intra= trim($this->accountancy_code_sell_intra); + $this->accountancy_code_sell_export= trim($this->accountancy_code_sell_export); $this->db->begin(); // Check name is required and codes are ok or unique. // If error, this->errors[] is filled - if ($action != 'add') - { - $result = $this->verify(); // We don't check when update called during a create because verify was already done + if ($action != 'add') { + $result = $this->verify(); // We don't check when update called during a create because verify was already done } - if ($result >= 0) - { - if (empty($this->oldcopy)) - { + if ($result >= 0) { + if (empty($this->oldcopy)) { $org=new self($this->db); $org->fetch($this->id); $this->oldcopy=$org; @@ -831,8 +851,7 @@ class Product extends CommonObject // Test if batch management is activated on existing product // If yes, we create missing entries into product_batch - if ($this->hasbatch() && !$this->oldcopy->hasbatch()) - { + if ($this->hasbatch() && !$this->oldcopy->hasbatch()) { //$valueforundefinedlot = 'Undefined'; // In previous version, 39 and lower $valueforundefinedlot = '000000'; @@ -844,13 +863,11 @@ class Product extends CommonObject $qty_batch = 0; foreach ($ObjW->detail_batch as $detail) // Each lines of detail in product_batch of the current $ObjW = product_stock { - if ($detail->batch == $valueforundefinedlot || $detail->batch == 'Undefined') - { + if ($detail->batch == $valueforundefinedlot || $detail->batch == 'Undefined') { // We discard this line, we will create it later $sqlclean="DELETE FROM ".MAIN_DB_PREFIX."product_batch WHERE batch in('Undefined', '".$valueforundefinedlot."') AND fk_product_stock = ".$ObjW->id; $result = $this->db->query($sqlclean); - if (! $result) - { + if (! $result) { dol_print_error($this->db); exit; } @@ -861,15 +878,13 @@ class Product extends CommonObject } // Quantities in batch details are not same as stock quantity, // so we add a default batch record to complete and get same qty in parent and child table - if ($ObjW->real <> $qty_batch) - { + if ($ObjW->real <> $qty_batch) { $ObjBatch = new Productbatch($this->db); $ObjBatch->batch = $valueforundefinedlot; $ObjBatch->qty = ($ObjW->real - $qty_batch); $ObjBatch->fk_product_stock = $ObjW->id; - if ($ObjBatch->create($user,1) < 0) - { + if ($ObjBatch->create($user, 1) < 0) { $error++; $this->errors=$ObjBatch->errors; } @@ -877,708 +892,678 @@ class Product extends CommonObject } } - // For automatic creation - if ($this->barcode == -1) $this->barcode = $this->get_barcode($this,$this->barcode_type_code); + // For automatic creation + if ($this->barcode == -1) { $this->barcode = $this->get_barcode($this, $this->barcode_type_code); + } - $sql = "UPDATE ".MAIN_DB_PREFIX."product"; - $sql.= " SET label = '" . $this->db->escape($this->label) ."'"; - $sql.= ", ref = '" . $this->db->escape($this->ref) ."'"; - $sql.= ", ref_ext = ".(! empty($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null"); - $sql.= ", default_vat_code = ".($this->default_vat_code ? "'".$this->db->escape($this->default_vat_code)."'" : "null"); - $sql.= ", tva_tx = " . $this->tva_tx; - $sql.= ", recuperableonly = " . $this->tva_npr; - $sql.= ", localtax1_tx = " . $this->localtax1_tx; - $sql.= ", localtax2_tx = " . $this->localtax2_tx; - $sql.= ", localtax1_type = " . ($this->localtax1_type!=''?"'".$this->db->escape($this->localtax1_type)."'":"'0'"); - $sql.= ", localtax2_type = " . ($this->localtax2_type!=''?"'".$this->db->escape($this->localtax2_type)."'":"'0'"); + $sql = "UPDATE ".MAIN_DB_PREFIX."product"; + $sql.= " SET label = '" . $this->db->escape($this->label) ."'"; + $sql.= ", ref = '" . $this->db->escape($this->ref) ."'"; + $sql.= ", ref_ext = ".(! empty($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null"); + $sql.= ", default_vat_code = ".($this->default_vat_code ? "'".$this->db->escape($this->default_vat_code)."'" : "null"); + $sql.= ", tva_tx = " . $this->tva_tx; + $sql.= ", recuperableonly = " . $this->tva_npr; + $sql.= ", localtax1_tx = " . $this->localtax1_tx; + $sql.= ", localtax2_tx = " . $this->localtax2_tx; + $sql.= ", localtax1_type = " . ($this->localtax1_type!=''?"'".$this->db->escape($this->localtax1_type)."'":"'0'"); + $sql.= ", localtax2_type = " . ($this->localtax2_type!=''?"'".$this->db->escape($this->localtax2_type)."'":"'0'"); - $sql.= ", barcode = ". (empty($this->barcode)?"null":"'".$this->db->escape($this->barcode)."'"); - $sql.= ", fk_barcode_type = ". (empty($this->barcode_type)?"null":$this->db->escape($this->barcode_type)); + $sql.= ", barcode = ". (empty($this->barcode)?"null":"'".$this->db->escape($this->barcode)."'"); + $sql.= ", fk_barcode_type = ". (empty($this->barcode_type)?"null":$this->db->escape($this->barcode_type)); - $sql.= ", tosell = " . $this->status; - $sql.= ", tobuy = " . $this->status_buy; - $sql.= ", tobatch = " . ((empty($this->status_batch) || $this->status_batch < 0) ? '0' : $this->status_batch); - $sql.= ", finished = " . ((! isset($this->finished) || $this->finished < 0) ? "null" : (int) $this->finished); - $sql.= ", weight = " . ($this->weight!='' ? "'".$this->db->escape($this->weight)."'" : 'null'); - $sql.= ", weight_units = " . ($this->weight_units!='' ? "'".$this->db->escape($this->weight_units)."'": 'null'); - $sql.= ", length = " . ($this->length!='' ? "'".$this->db->escape($this->length)."'" : 'null'); - $sql.= ", length_units = " . ($this->length_units!='' ? "'".$this->db->escape($this->length_units)."'" : 'null'); - $sql.= ", width= " . ($this->width!='' ? "'".$this->db->escape($this->width)."'" : 'null'); - $sql.= ", width_units = " . ($this->width_units!='' ? "'".$this->db->escape($this->width_units)."'" : 'null'); - $sql.= ", height = " . ($this->height!='' ? "'".$this->db->escape($this->height)."'" : 'null'); - $sql.= ", height_units = " . ($this->height_units!='' ? "'".$this->db->escape($this->height_units)."'" : 'null'); - $sql.= ", surface = " . ($this->surface!='' ? "'".$this->db->escape($this->surface)."'" : 'null'); - $sql.= ", surface_units = " . ($this->surface_units!='' ? "'".$this->db->escape($this->surface_units)."'" : 'null'); - $sql.= ", volume = " . ($this->volume!='' ? "'".$this->db->escape($this->volume)."'" : 'null'); - $sql.= ", volume_units = " . ($this->volume_units!='' ? "'".$this->db->escape($this->volume_units)."'" : 'null'); - $sql.= ", fk_default_warehouse = " . ($this->fk_default_warehouse > 0 ? $this->db->escape($this->fk_default_warehouse) : 'null'); - $sql.= ", seuil_stock_alerte = " . ((isset($this->seuil_stock_alerte) && $this->seuil_stock_alerte != '') ? "'".$this->db->escape($this->seuil_stock_alerte)."'" : "null"); - $sql.= ", description = '" . $this->db->escape($this->description) ."'"; - $sql.= ", url = " . ($this->url?"'".$this->db->escape($this->url)."'":'null'); - $sql.= ", customcode = '" . $this->db->escape($this->customcode) ."'"; - $sql.= ", fk_country = " . ($this->country_id > 0 ? $this->country_id : 'null'); - $sql.= ", note = ".(isset($this->note) ? "'" .$this->db->escape($this->note)."'" : 'null'); - $sql.= ", duration = '" . $this->db->escape($this->duration_value . $this->duration_unit) ."'"; - $sql.= ", accountancy_code_buy = '" . $this->db->escape($this->accountancy_code_buy)."'"; - $sql.= ", accountancy_code_sell= '" . $this->db->escape($this->accountancy_code_sell)."'"; - $sql.= ", accountancy_code_sell_intra= '" . $this->db->escape($this->accountancy_code_sell_intra)."'"; - $sql.= ", accountancy_code_sell_export= '" . $this->db->escape($this->accountancy_code_sell_export)."'"; - $sql.= ", desiredstock = " . ((isset($this->desiredstock) && $this->desiredstock != '') ? $this->desiredstock : "null"); - $sql.= ", cost_price = " . ($this->cost_price != '' ? $this->db->escape($this->cost_price) : 'null'); - $sql.= ", fk_unit= " . (!$this->fk_unit ? 'NULL' : $this->fk_unit); - $sql.= ", price_autogen = " . (!$this->price_autogen ? 0 : 1); - $sql.= ", fk_price_expression = ".($this->fk_price_expression != 0 ? $this->fk_price_expression : 'NULL'); - $sql.= ", fk_user_modif = ".($user->id > 0 ? $user->id : 'NULL'); - // stock field is not here because it is a denormalized value from product_stock. - $sql.= " WHERE rowid = " . $id; + $sql.= ", tosell = " . $this->status; + $sql.= ", tobuy = " . $this->status_buy; + $sql.= ", tobatch = " . ((empty($this->status_batch) || $this->status_batch < 0) ? '0' : $this->status_batch); + $sql.= ", finished = " . ((! isset($this->finished) || $this->finished < 0) ? "null" : (int) $this->finished); + $sql.= ", weight = " . ($this->weight!='' ? "'".$this->db->escape($this->weight)."'" : 'null'); + $sql.= ", weight_units = " . ($this->weight_units!='' ? "'".$this->db->escape($this->weight_units)."'": 'null'); + $sql.= ", length = " . ($this->length!='' ? "'".$this->db->escape($this->length)."'" : 'null'); + $sql.= ", length_units = " . ($this->length_units!='' ? "'".$this->db->escape($this->length_units)."'" : 'null'); + $sql.= ", width= " . ($this->width!='' ? "'".$this->db->escape($this->width)."'" : 'null'); + $sql.= ", width_units = " . ($this->width_units!='' ? "'".$this->db->escape($this->width_units)."'" : 'null'); + $sql.= ", height = " . ($this->height!='' ? "'".$this->db->escape($this->height)."'" : 'null'); + $sql.= ", height_units = " . ($this->height_units!='' ? "'".$this->db->escape($this->height_units)."'" : 'null'); + $sql.= ", surface = " . ($this->surface!='' ? "'".$this->db->escape($this->surface)."'" : 'null'); + $sql.= ", surface_units = " . ($this->surface_units!='' ? "'".$this->db->escape($this->surface_units)."'" : 'null'); + $sql.= ", volume = " . ($this->volume!='' ? "'".$this->db->escape($this->volume)."'" : 'null'); + $sql.= ", volume_units = " . ($this->volume_units!='' ? "'".$this->db->escape($this->volume_units)."'" : 'null'); + $sql.= ", fk_default_warehouse = " . ($this->fk_default_warehouse > 0 ? $this->db->escape($this->fk_default_warehouse) : 'null'); + $sql.= ", seuil_stock_alerte = " . ((isset($this->seuil_stock_alerte) && $this->seuil_stock_alerte != '') ? "'".$this->db->escape($this->seuil_stock_alerte)."'" : "null"); + $sql.= ", description = '" . $this->db->escape($this->description) ."'"; + $sql.= ", url = " . ($this->url?"'".$this->db->escape($this->url)."'":'null'); + $sql.= ", customcode = '" . $this->db->escape($this->customcode) ."'"; + $sql.= ", fk_country = " . ($this->country_id > 0 ? $this->country_id : 'null'); + $sql.= ", note = ".(isset($this->note) ? "'" .$this->db->escape($this->note)."'" : 'null'); + $sql.= ", duration = '" . $this->db->escape($this->duration_value . $this->duration_unit) ."'"; + $sql.= ", accountancy_code_buy = '" . $this->db->escape($this->accountancy_code_buy)."'"; + $sql.= ", accountancy_code_sell= '" . $this->db->escape($this->accountancy_code_sell)."'"; + $sql.= ", accountancy_code_sell_intra= '" . $this->db->escape($this->accountancy_code_sell_intra)."'"; + $sql.= ", accountancy_code_sell_export= '" . $this->db->escape($this->accountancy_code_sell_export)."'"; + $sql.= ", desiredstock = " . ((isset($this->desiredstock) && $this->desiredstock != '') ? $this->desiredstock : "null"); + $sql.= ", cost_price = " . ($this->cost_price != '' ? $this->db->escape($this->cost_price) : 'null'); + $sql.= ", fk_unit= " . (!$this->fk_unit ? 'NULL' : $this->fk_unit); + $sql.= ", price_autogen = " . (!$this->price_autogen ? 0 : 1); + $sql.= ", fk_price_expression = ".($this->fk_price_expression != 0 ? $this->fk_price_expression : 'NULL'); + $sql.= ", fk_user_modif = ".($user->id > 0 ? $user->id : 'NULL'); + // stock field is not here because it is a denormalized value from product_stock. + $sql.= " WHERE rowid = " . $id; - dol_syslog(get_class($this)."::update", LOG_DEBUG); + dol_syslog(get_class($this)."::update", LOG_DEBUG); - $resql=$this->db->query($sql); - if ($resql) - { - $this->id = $id; + $resql=$this->db->query($sql); + if ($resql) { + $this->id = $id; - // Multilangs - if (! empty($conf->global->MAIN_MULTILANGS)) - { - if ( $this->setMultiLangs($user) < 0) - { - $this->error=$langs->trans("Error")." : ".$this->db->error()." - ".$sql; - return -2; - } - } + // Multilangs + if (! empty($conf->global->MAIN_MULTILANGS)) { + if ($this->setMultiLangs($user) < 0) { + $this->error=$langs->trans("Error")." : ".$this->db->error()." - ".$sql; + return -2; + } + } - $action='update'; + $action='update'; - // Actions on extra fields - if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) - { - $result=$this->insertExtraFields(); - if ($result < 0) - { - $error++; - } - } + // Actions on extra fields + if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) { + $result=$this->insertExtraFields(); + if ($result < 0) { + $error++; + } + } - if (! $error && ! $notrigger) - { - // Call trigger - $result=$this->call_trigger('PRODUCT_MODIFY',$user); - if ($result < 0) { $error++; } + if (! $error && ! $notrigger) { + // Call trigger + $result=$this->call_trigger('PRODUCT_MODIFY', $user); + if ($result < 0) { $error++; + } // End call triggers - } + } - if (! $error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) - { - // We remove directory - if ($conf->product->dir_output) - { - $olddir = $conf->product->dir_output . "/" . dol_sanitizeFileName($this->oldcopy->ref); - $newdir = $conf->product->dir_output . "/" . dol_sanitizeFileName($this->ref); - if (file_exists($olddir)) - { - //include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; - //$res = dol_move($olddir, $newdir); - // do not use dol_move with directory - $res = @rename($olddir, $newdir); - if (! $res) - { - $langs->load("errors"); - $this->error=$langs->trans('ErrorFailToRenameDir',$olddir,$newdir); - $error++; - } - } - } - } + if (! $error && (is_object($this->oldcopy) && $this->oldcopy->ref !== $this->ref)) { + // We remove directory + if ($conf->product->dir_output) { + $olddir = $conf->product->dir_output . "/" . dol_sanitizeFileName($this->oldcopy->ref); + $newdir = $conf->product->dir_output . "/" . dol_sanitizeFileName($this->ref); + if (file_exists($olddir)) { + //include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + //$res = dol_move($olddir, $newdir); + // do not use dol_move with directory + $res = @rename($olddir, $newdir); + if (! $res) { + $langs->load("errors"); + $this->error=$langs->trans('ErrorFailToRenameDir', $olddir, $newdir); + $error++; + } + } + } + } - if (! $error) - { - if ($conf->variants->enabled) { + if (! $error) { + if ($conf->variants->enabled) { - require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php'; + include_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php'; - $comb = new ProductCombination($this->db); + $comb = new ProductCombination($this->db); - foreach ($comb->fetchAllByFkProductParent($this->id) as $currcomb) { - $currcomb->updateProperties($this); - } - } + foreach ($comb->fetchAllByFkProductParent($this->id) as $currcomb) { + $currcomb->updateProperties($this); + } + } - $this->db->commit(); - return 1; - } - else - { - $this->db->rollback(); - return -$error; - } - } - else - { - if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') - { - $langs->load("errors"); - if (empty($conf->barcode->enabled) || empty($this->barcode)) $this->error=$langs->trans("Error")." : ".$langs->trans("ErrorProductAlreadyExists",$this->ref); - else $this->error=$langs->trans("Error")." : ".$langs->trans("ErrorProductBarCodeAlreadyExists",$this->barcode); - $this->errors[]=$this->error; - $this->db->rollback(); - return -1; - } - else - { - $this->error=$langs->trans("Error")." : ".$this->db->error()." - ".$sql; - $this->errors[]=$this->error; - $this->db->rollback(); - return -2; - } - } + $this->db->commit(); + return 1; + } + else + { + $this->db->rollback(); + return -$error; + } + } + else + { + if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { + $langs->load("errors"); + if (empty($conf->barcode->enabled) || empty($this->barcode)) { $this->error=$langs->trans("Error")." : ".$langs->trans("ErrorProductAlreadyExists", $this->ref); + } else { $this->error=$langs->trans("Error")." : ".$langs->trans("ErrorProductBarCodeAlreadyExists", $this->barcode); + } + $this->errors[]=$this->error; + $this->db->rollback(); + return -1; + } + else + { + $this->error=$langs->trans("Error")." : ".$this->db->error()." - ".$sql; + $this->errors[]=$this->error; + $this->db->rollback(); + return -2; + } + } } else - { + { $this->db->rollback(); - dol_syslog(get_class($this)."::Update fails verify ".join(',',$this->errors), LOG_WARNING); + dol_syslog(get_class($this)."::Update fails verify ".join(',', $this->errors), LOG_WARNING); return -3; } - } + } - /** - * Delete a product from database (if not used) - * - * @param User $user Product id (usage of this is deprecated, delete should be called without parameters on a fetched object) - * @param int $notrigger Do not execute trigger - * @return int < 0 if KO, 0 = Not possible, > 0 if OK - */ - function delete(User $user, $notrigger=0) - { - // Deprecation warning - if ($id > 0) { - dol_syslog(__METHOD__ . " with parameter is deprecated", LOG_WARNING); - } + /** + * Delete a product from database (if not used) + * + * @param User $user Product id (usage of this is deprecated, delete should be called without parameters on a fetched object) + * @param int $notrigger Do not execute trigger + * @return int < 0 if KO, 0 = Not possible, > 0 if OK + */ + function delete(User $user, $notrigger=0) + { + // Deprecation warning + if ($id > 0) { + dol_syslog(__METHOD__ . " with parameter is deprecated", LOG_WARNING); + } - global $conf, $langs; - require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + global $conf, $langs; + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; - $error=0; + $error=0; - // Clean parameters - if (empty($id)) $id=$this->id; - else $this->fetch($id); + // Clean parameters + if (empty($id)) { $id=$this->id; + } else { $this->fetch($id); + } - // Check parameters - if (empty($id)) - { - $this->error = "Object must be fetched before calling delete"; - return -1; - } - if (($this->type == Product::TYPE_PRODUCT && empty($user->rights->produit->supprimer)) || ($this->type == Product::TYPE_SERVICE && empty($user->rights->service->supprimer))) - { - $this->error = "ErrorForbidden"; - return 0; - } + // Check parameters + if (empty($id)) { + $this->error = "Object must be fetched before calling delete"; + return -1; + } + if (($this->type == Product::TYPE_PRODUCT && empty($user->rights->produit->supprimer)) || ($this->type == Product::TYPE_SERVICE && empty($user->rights->service->supprimer))) { + $this->error = "ErrorForbidden"; + return 0; + } - $objectisused = $this->isObjectUsed($id); - if (empty($objectisused)) - { - $this->db->begin(); + $objectisused = $this->isObjectUsed($id); + if (empty($objectisused)) { + $this->db->begin(); - if (! $error && empty($notrigger)) - { + if (! $error && empty($notrigger)) { // Call trigger - $result=$this->call_trigger('PRODUCT_DELETE',$user); - if ($result < 0) { $error++; } + $result=$this->call_trigger('PRODUCT_DELETE', $user); + if ($result < 0) { $error++; + } // End call triggers - } + } - // Delete from product_batch on product delete - if (! $error) - { - $sql = "DELETE FROM ".MAIN_DB_PREFIX.'product_batch'; - $sql.= " WHERE fk_product_stock IN ("; - $sql.= "SELECT rowid FROM ".MAIN_DB_PREFIX.'product_stock'; - $sql.= " WHERE fk_product = ".$id.")"; + // Delete from product_batch on product delete + if (! $error) { + $sql = "DELETE FROM ".MAIN_DB_PREFIX.'product_batch'; + $sql.= " WHERE fk_product_stock IN ("; + $sql.= "SELECT rowid FROM ".MAIN_DB_PREFIX.'product_stock'; + $sql.= " WHERE fk_product = ".$id.")"; - $result = $this->db->query($sql); - if (! $result) - { - $error++; - $this->errors[] = $this->db->lasterror(); - } - } + $result = $this->db->query($sql); + if (! $result) { + $error++; + $this->errors[] = $this->db->lasterror(); + } + } - // Delete all child tables - if (! $error) - { - $elements = array('product_fournisseur_price','product_price','product_lang','categorie_product','product_stock','product_customer_price','product_lot'); // product_batch is done before - foreach($elements as $table) - { - if (! $error) - { - $sql = "DELETE FROM ".MAIN_DB_PREFIX.$table; - $sql.= " WHERE fk_product = ".$id; - - $result = $this->db->query($sql); - if (! $result) - { - $error++; - $this->errors[] = $this->db->lasterror(); - } - } - } - } - - if (!$error) { - - require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php'; - require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination2ValuePair.class.php'; - - //If it is a parent product, then we remove the association with child products - $prodcomb = new ProductCombination($this->db); - - if ($prodcomb->deleteByFkProductParent($user, $id) < 0) { - $error++; - $this->errors[] = 'Error deleting combinations'; - } - - //We also check if it is a child product - if (!$error && ($prodcomb->fetchByFkProductChild($id) > 0) && ($prodcomb->delete($user) < 0)) { - $error++; - $this->errors[] = 'Error deleting child combination'; - } - } - - // Delete from product_association - if (!$error){ - $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_association"; - $sql.= " WHERE fk_product_pere = ".$id." OR fk_product_fils = ".$id; - - $result = $this->db->query($sql); - if (! $result) - { - $error++; - $this->errors[] = $this->db->lasterror(); - } - } - - // Delete product - if (! $error) - { - $sqlz = "DELETE FROM ".MAIN_DB_PREFIX."product"; - $sqlz.= " WHERE rowid = ".$id; - - $resultz = $this->db->query($sqlz); - if ( ! $resultz ) - { - $error++; - $this->errors[] = $this->db->lasterror(); - } - } - - if (! $error) - { - // We remove directory - $ref = dol_sanitizeFileName($this->ref); - if ($conf->product->dir_output) - { - $dir = $conf->product->dir_output . "/" . $ref; - if (file_exists($dir)) - { - $res=@dol_delete_dir_recursive($dir); - if (! $res) - { - $this->errors[] = 'ErrorFailToDeleteDir'; - $error++; - } - } - } - } - - // Remove extrafields - if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used - { - $result=$this->deleteExtraFields(); - if ($result < 0) - { - $error++; - dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR); - } - } - - if (! $error) - { - $this->db->commit(); - return 1; - } - else - { - foreach($this->errors as $errmsg) - { - dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR); - $this->error.=($this->error?', '.$errmsg:$errmsg); - } - $this->db->rollback(); - return -$error; - } - } - else - { - $this->error = "ErrorRecordIsUsedCantDelete"; - return 0; - } - } - - /** - * Update or add a translation for a product - * - * @param User $user Object user making update - * @return int <0 if KO, >0 if OK - */ - function setMultiLangs($user) - { - global $conf, $langs; - - $langs_available = $langs->get_available_languages(DOL_DOCUMENT_ROOT, 0, 2); - $current_lang = $langs->getDefaultLang(); - - foreach ($langs_available as $key => $value) - { - if ($key == $current_lang) - { - $sql = "SELECT rowid"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_lang"; - $sql.= " WHERE fk_product=".$this->id; - $sql.= " AND lang='".$key."'"; - - $result = $this->db->query($sql); - - if ($this->db->num_rows($result)) // if there is already a description line for this language - { - $sql2 = "UPDATE ".MAIN_DB_PREFIX."product_lang"; - $sql2.= " SET "; - $sql2.= " label='".$this->db->escape($this->label)."',"; - $sql2.= " description='".$this->db->escape($this->description)."'"; - if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) $sql2.= ", note='".$this->db->escape($this->other)."'"; - $sql2.= " WHERE fk_product=".$this->id." AND lang='".$this->db->escape($key)."'"; - } - else - { - $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."product_lang (fk_product, lang, label, description"; - if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) $sql2.=", note"; - $sql2.= ")"; - $sql2.= " VALUES(".$this->id.",'".$this->db->escape($key)."','". $this->db->escape($this->label)."',"; - $sql2.= " '".$this->db->escape($this->description)."'"; - if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) $sql2.= ", '".$this->db->escape($this->other)."'"; - $sql2.= ")"; - } - dol_syslog(get_class($this).'::setMultiLangs key = current_lang = '.$key); - if (! $this->db->query($sql2)) - { - $this->error=$this->db->lasterror(); - return -1; - } - } - else if (isset($this->multilangs[$key])) - { - $sql = "SELECT rowid"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_lang"; - $sql.= " WHERE fk_product=".$this->id; - $sql.= " AND lang='".$key."'"; - - $result = $this->db->query($sql); - - if ($this->db->num_rows($result)) // if there is already a description line for this language - { - $sql2 = "UPDATE ".MAIN_DB_PREFIX."product_lang"; - $sql2.= " SET "; - $sql2.= " label='".$this->db->escape($this->multilangs["$key"]["label"])."',"; - $sql2.= " description='".$this->db->escape($this->multilangs["$key"]["description"])."'"; - if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) $sql2.= ", note='".$this->db->escape($this->multilangs["$key"]["other"])."'"; - $sql2.= " WHERE fk_product=".$this->id." AND lang='".$this->db->escape($key)."'"; - } - else - { - $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."product_lang (fk_product, lang, label, description"; - if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) $sql2.=", note"; - $sql2.= ")"; - $sql2.= " VALUES(".$this->id.",'".$this->db->escape($key)."','". $this->db->escape($this->multilangs["$key"]["label"])."',"; - $sql2.= " '".$this->db->escape($this->multilangs["$key"]["description"])."'"; - if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) $sql2.= ", '".$this->db->escape($this->multilangs["$key"]["other"])."'"; - $sql2.= ")"; - } - - // We do not save if main fields are empty - if ($this->multilangs["$key"]["label"] || $this->multilangs["$key"]["description"]) - { - if (! $this->db->query($sql2)) - { - $this->error=$this->db->lasterror(); - return -1; - } - } - } - else - { - // language is not current language and we didn't provide a multilang description for this language - } - } - - // Call trigger - $result = $this->call_trigger('PRODUCT_SET_MULTILANGS',$user); - if ($result < 0) { - $this->error = $this->db->lasterror(); - return -1; - } - // End call triggers - - return 1; - } - - /** - * Delete a language for this product - * - * @param string $langtodelete Language code to delete - * @param User $user Object user making delete - * - * @return int <0 if KO, >0 if OK - */ - function delMultiLangs($langtodelete, $user) - { - $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_lang"; - $sql.= " WHERE fk_product=".$this->id." AND lang='".$this->db->escape($langtodelete)."'"; - - dol_syslog(get_class($this).'::delMultiLangs', LOG_DEBUG); - $result = $this->db->query($sql); - if ($result) - { - // Call trigger - $result = $this->call_trigger('PRODUCT_DEL_MULTILANGS',$user); - if ($result < 0) { - $this->error = $this->db->lasterror(); - dol_syslog(get_class($this).'::delMultiLangs error='.$this->error, LOG_ERR); - return -1; - } - // End call triggers - return 1; - } - else - { - $this->error=$this->db->lasterror(); - dol_syslog(get_class($this).'::delMultiLangs error='.$this->error, LOG_ERR); - return -1; - } - } - - /* - * Sets an accountancy code for a product. - * Also calls PRODUCT_MODIFY trigger when modified - * - * @param string $type It can be 'buy', 'sell', 'sell_intra' or 'sell_export' - * @param string $value Accountancy code - * @return int <0 KO >0 OK - */ - public function setAccountancyCode($type, $value) - { - global $user, $langs, $conf; - - $this->db->begin(); - - if ($type == 'buy') { - $field = 'accountancy_code_buy'; - } elseif ($type == 'sell') { - $field = 'accountancy_code_sell'; - } elseif ($type == 'sell_intra') { - $field = 'accountancy_code_sell_intra'; - } elseif ($type == 'sell_export') { - $field = 'accountancy_code_sell_export'; - } else { - return -1; - } - - $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET "; - $sql.= "$field = '".$this->db->escape($value)."'"; - $sql.= " WHERE rowid = ".$this->id; - - dol_syslog(get_class($this)."::".__FUNCTION__." sql=".$sql, LOG_DEBUG); - $resql = $this->db->query($sql); - - if ($resql) - { - // Call triggers - include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; - $interface=new Interfaces($this->db); - $result=$interface->run_triggers('PRODUCT_MODIFY',$this,$user,$langs,$conf); - if ($result < 0) - { - $this->errors=$interface->errors; - $this->db->rollback(); - return -1; - } - // End call triggers - - $this->$field = $value; - - $this->db->commit(); - return 1; - } - else - { - $this->error=$this->db->lasterror(); - $this->db->rollback(); - return -1; - } - } - - /** - * Load array this->multilangs - * - * @return int <0 if KO, >0 if OK - */ - function getMultiLangs() - { - global $langs; - - $current_lang = $langs->getDefaultLang(); - - $sql = "SELECT lang, label, description, note as other"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_lang"; - $sql.= " WHERE fk_product=".$this->id; - - $result = $this->db->query($sql); - if ($result) - { - while ($obj = $this->db->fetch_object($result)) - { - //print 'lang='.$obj->lang.' current='.$current_lang.'
'; - if ($obj->lang == $current_lang) // si on a les traduct. dans la langue courante on les charge en infos principales. - { - $this->label = $obj->label; - $this->description = $obj->description; - $this->other = $obj->other; - } - $this->multilangs["$obj->lang"]["label"] = $obj->label; - $this->multilangs["$obj->lang"]["description"] = $obj->description; - $this->multilangs["$obj->lang"]["other"] = $obj->other; - } - return 1; - } - else - { - $this->error="Error: ".$this->db->lasterror()." - ".$sql; - return -1; - } - } - - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Insert a track that we changed a customer price - * - * @param User $user User making change - * @param int $level price level to change - * @return int <0 if KO, >0 if OK - */ - function _log_price($user,$level=0) - { - // phpcs:enable - global $conf; - - $now=dol_now(); - - // Clean parameters - if (empty($this->price_by_qty)) $this->price_by_qty=0; - - // Add new price - $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_price(price_level,date_price, fk_product, fk_user_author, price, price_ttc, price_base_type,tosell, tva_tx, default_vat_code, recuperableonly,"; - $sql.= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, price_min,price_min_ttc,price_by_qty,entity,fk_price_expression) "; - $sql.= " VALUES(".($level?$level:1).", '".$this->db->idate($now)."',".$this->id.",".$user->id.",".$this->price.",".$this->price_ttc.",'".$this->db->escape($this->price_base_type)."',".$this->status.",".$this->tva_tx.", ".($this->default_vat_code?("'".$this->db->escape($this->default_vat_code)."'"):"null").",".$this->tva_npr.","; - $sql.= " ".$this->localtax1_tx.", ".$this->localtax2_tx.", '".$this->db->escape($this->localtax1_type)."', '".$this->db->escape($this->localtax2_type)."', ".$this->price_min.",".$this->price_min_ttc.",".$this->price_by_qty.",".$conf->entity.",".($this->fk_price_expression > 0?$this->fk_price_expression:'null'); - $sql.= ")"; - - dol_syslog(get_class($this)."::_log_price", LOG_DEBUG); - $resql=$this->db->query($sql); - if(! $resql) - { - $this->error=$this->db->lasterror(); - dol_print_error($this->db); - return -1; - } - else - { - return 1; - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Delete a price line - * - * @param User $user Object user - * @param int $rowid Line id to delete - * @return int <0 if KO, >0 if OK - */ - function log_price_delete($user, $rowid) - { - // phpcs:enable - $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_price_by_qty"; - $sql.= " WHERE fk_product_price=".$rowid; - $resql=$this->db->query($sql); - - $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_price"; - $sql.= " WHERE rowid=".$rowid; - $resql=$this->db->query($sql); - if ($resql) - { - return 1; - } - else - { - $this->error=$this->db->lasterror(); - return -1; - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Read price used by a provider. - * We enter as input couple prodfournprice/qty or triplet qty/product_id/fourn_ref. - * This also set some properties on product like ->buyprice, ->fourn_pu, ... - * - * @param int $prodfournprice Id du tarif = rowid table product_fournisseur_price - * @param double $qty Quantity asked or -1 to get first entry found - * @param int $product_id Filter on a particular product id - * @param string $fourn_ref Filter on a supplier price ref. 'none' to exclude ref in search. - * @param int $fk_soc If of supplier - * @return int <-1 if KO, -1 if qty not enough, 0 if OK but nothing found, id_product if OK and found. May also initialize some properties like (->ref_supplier, buyprice, fourn_pu, vatrate_supplier...) - */ - function get_buyprice($prodfournprice, $qty, $product_id=0, $fourn_ref='', $fk_soc=0) - { - // phpcs:enable - global $conf; - $result = 0; - - // We do a first seach with a select by searching with couple prodfournprice and qty only (later we will search on triplet qty/product_id/fourn_ref) - $sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.remise_percent,"; - $sql.= " pfp.fk_product, pfp.ref_fourn, pfp.desc_fourn, pfp.fk_soc, pfp.tva_tx, pfp.fk_supplier_price_expression"; - $sql.= " ,pfp.default_vat_code"; - $sql.= " ,pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; - $sql.= " WHERE pfp.rowid = ".$prodfournprice; - if ($qty > 0) $sql.= " AND pfp.quantity <= ".$qty; - $sql.= " ORDER BY pfp.quantity DESC"; - - dol_syslog(get_class($this)."::get_buyprice first search by prodfournprice/qty", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - $obj = $this->db->fetch_object($resql); - if ($obj && $obj->quantity > 0) // If we found a supplier prices from the id of supplier price - { - if (!empty($conf->dynamicprices->enabled) && !empty($obj->fk_supplier_price_expression)) + // Delete all child tables + if (! $error) { + $elements = array('product_fournisseur_price','product_price','product_lang','categorie_product','product_stock','product_customer_price','product_lot'); // product_batch is done before + foreach($elements as $table) { - require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; + if (! $error) { + $sql = "DELETE FROM ".MAIN_DB_PREFIX.$table; + $sql.= " WHERE fk_product = ".$id; + + $result = $this->db->query($sql); + if (! $result) { + $error++; + $this->errors[] = $this->db->lasterror(); + } + } + } + } + + if (!$error) { + + include_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php'; + include_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination2ValuePair.class.php'; + + //If it is a parent product, then we remove the association with child products + $prodcomb = new ProductCombination($this->db); + + if ($prodcomb->deleteByFkProductParent($user, $id) < 0) { + $error++; + $this->errors[] = 'Error deleting combinations'; + } + + //We also check if it is a child product + if (!$error && ($prodcomb->fetchByFkProductChild($id) > 0) && ($prodcomb->delete($user) < 0)) { + $error++; + $this->errors[] = 'Error deleting child combination'; + } + } + + // Delete from product_association + if (!$error) { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_association"; + $sql.= " WHERE fk_product_pere = ".$id." OR fk_product_fils = ".$id; + + $result = $this->db->query($sql); + if (! $result) { + $error++; + $this->errors[] = $this->db->lasterror(); + } + } + + // Delete product + if (! $error) { + $sqlz = "DELETE FROM ".MAIN_DB_PREFIX."product"; + $sqlz.= " WHERE rowid = ".$id; + + $resultz = $this->db->query($sqlz); + if (! $resultz ) { + $error++; + $this->errors[] = $this->db->lasterror(); + } + } + + if (! $error) { + // We remove directory + $ref = dol_sanitizeFileName($this->ref); + if ($conf->product->dir_output) { + $dir = $conf->product->dir_output . "/" . $ref; + if (file_exists($dir)) { + $res=@dol_delete_dir_recursive($dir); + if (! $res) { + $this->errors[] = 'ErrorFailToDeleteDir'; + $error++; + } + } + } + } + + // Remove extrafields + if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used + { + $result=$this->deleteExtraFields(); + if ($result < 0) { + $error++; + dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR); + } + } + + if (! $error) { + $this->db->commit(); + return 1; + } + else + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -$error; + } + } + else + { + $this->error = "ErrorRecordIsUsedCantDelete"; + return 0; + } + } + + /** + * Update or add a translation for a product + * + * @param User $user Object user making update + * @return int <0 if KO, >0 if OK + */ + function setMultiLangs($user) + { + global $conf, $langs; + + $langs_available = $langs->get_available_languages(DOL_DOCUMENT_ROOT, 0, 2); + $current_lang = $langs->getDefaultLang(); + + foreach ($langs_available as $key => $value) + { + if ($key == $current_lang) { + $sql = "SELECT rowid"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_lang"; + $sql.= " WHERE fk_product=".$this->id; + $sql.= " AND lang='".$key."'"; + + $result = $this->db->query($sql); + + if ($this->db->num_rows($result)) // if there is already a description line for this language + { + $sql2 = "UPDATE ".MAIN_DB_PREFIX."product_lang"; + $sql2.= " SET "; + $sql2.= " label='".$this->db->escape($this->label)."',"; + $sql2.= " description='".$this->db->escape($this->description)."'"; + if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) { $sql2.= ", note='".$this->db->escape($this->other)."'"; + } + $sql2.= " WHERE fk_product=".$this->id." AND lang='".$this->db->escape($key)."'"; + } + else + { + $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."product_lang (fk_product, lang, label, description"; + if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) { $sql2.=", note"; + } + $sql2.= ")"; + $sql2.= " VALUES(".$this->id.",'".$this->db->escape($key)."','". $this->db->escape($this->label)."',"; + $sql2.= " '".$this->db->escape($this->description)."'"; + if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) { $sql2.= ", '".$this->db->escape($this->other)."'"; + } + $sql2.= ")"; + } + dol_syslog(get_class($this).'::setMultiLangs key = current_lang = '.$key); + if (! $this->db->query($sql2)) { + $this->error=$this->db->lasterror(); + return -1; + } + } + else if (isset($this->multilangs[$key])) { + $sql = "SELECT rowid"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_lang"; + $sql.= " WHERE fk_product=".$this->id; + $sql.= " AND lang='".$key."'"; + + $result = $this->db->query($sql); + + if ($this->db->num_rows($result)) // if there is already a description line for this language + { + $sql2 = "UPDATE ".MAIN_DB_PREFIX."product_lang"; + $sql2.= " SET "; + $sql2.= " label='".$this->db->escape($this->multilangs["$key"]["label"])."',"; + $sql2.= " description='".$this->db->escape($this->multilangs["$key"]["description"])."'"; + if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) { $sql2.= ", note='".$this->db->escape($this->multilangs["$key"]["other"])."'"; + } + $sql2.= " WHERE fk_product=".$this->id." AND lang='".$this->db->escape($key)."'"; + } + else + { + $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."product_lang (fk_product, lang, label, description"; + if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) { $sql2.=", note"; + } + $sql2.= ")"; + $sql2.= " VALUES(".$this->id.",'".$this->db->escape($key)."','". $this->db->escape($this->multilangs["$key"]["label"])."',"; + $sql2.= " '".$this->db->escape($this->multilangs["$key"]["description"])."'"; + if (! empty($conf->global->PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION)) { $sql2.= ", '".$this->db->escape($this->multilangs["$key"]["other"])."'"; + } + $sql2.= ")"; + } + + // We do not save if main fields are empty + if ($this->multilangs["$key"]["label"] || $this->multilangs["$key"]["description"]) { + if (! $this->db->query($sql2)) { + $this->error=$this->db->lasterror(); + return -1; + } + } + } + else + { + // language is not current language and we didn't provide a multilang description for this language + } + } + + // Call trigger + $result = $this->call_trigger('PRODUCT_SET_MULTILANGS', $user); + if ($result < 0) { + $this->error = $this->db->lasterror(); + return -1; + } + // End call triggers + + return 1; + } + + /** + * Delete a language for this product + * + * @param string $langtodelete Language code to delete + * @param User $user Object user making delete + * + * @return int <0 if KO, >0 if OK + */ + function delMultiLangs($langtodelete, $user) + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_lang"; + $sql.= " WHERE fk_product=".$this->id." AND lang='".$this->db->escape($langtodelete)."'"; + + dol_syslog(get_class($this).'::delMultiLangs', LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + // Call trigger + $result = $this->call_trigger('PRODUCT_DEL_MULTILANGS', $user); + if ($result < 0) { + $this->error = $this->db->lasterror(); + dol_syslog(get_class($this).'::delMultiLangs error='.$this->error, LOG_ERR); + return -1; + } + // End call triggers + return 1; + } + else + { + $this->error=$this->db->lasterror(); + dol_syslog(get_class($this).'::delMultiLangs error='.$this->error, LOG_ERR); + return -1; + } + } + + /* + * Sets an accountancy code for a product. + * Also calls PRODUCT_MODIFY trigger when modified + * + * @param string $type It can be 'buy', 'sell', 'sell_intra' or 'sell_export' + * @param string $value Accountancy code + * @return int <0 KO >0 OK + */ + public function setAccountancyCode($type, $value) + { + global $user, $langs, $conf; + + $this->db->begin(); + + if ($type == 'buy') { + $field = 'accountancy_code_buy'; + } elseif ($type == 'sell') { + $field = 'accountancy_code_sell'; + } elseif ($type == 'sell_intra') { + $field = 'accountancy_code_sell_intra'; + } elseif ($type == 'sell_export') { + $field = 'accountancy_code_sell_export'; + } else { + return -1; + } + + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET "; + $sql.= "$field = '".$this->db->escape($value)."'"; + $sql.= " WHERE rowid = ".$this->id; + + dol_syslog(get_class($this)."::".__FUNCTION__." sql=".$sql, LOG_DEBUG); + $resql = $this->db->query($sql); + + if ($resql) { + // Call triggers + include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + $interface=new Interfaces($this->db); + $result=$interface->run_triggers('PRODUCT_MODIFY', $this, $user, $langs, $conf); + if ($result < 0) { + $this->errors=$interface->errors; + $this->db->rollback(); + return -1; + } + // End call triggers + + $this->$field = $value; + + $this->db->commit(); + return 1; + } + else + { + $this->error=$this->db->lasterror(); + $this->db->rollback(); + return -1; + } + } + + /** + * Load array this->multilangs + * + * @return int <0 if KO, >0 if OK + */ + function getMultiLangs() + { + global $langs; + + $current_lang = $langs->getDefaultLang(); + + $sql = "SELECT lang, label, description, note as other"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_lang"; + $sql.= " WHERE fk_product=".$this->id; + + $result = $this->db->query($sql); + if ($result) { + while ($obj = $this->db->fetch_object($result)) + { + //print 'lang='.$obj->lang.' current='.$current_lang.'
'; + if ($obj->lang == $current_lang) // si on a les traduct. dans la langue courante on les charge en infos principales. + { + $this->label = $obj->label; + $this->description = $obj->description; + $this->other = $obj->other; + } + $this->multilangs["$obj->lang"]["label"] = $obj->label; + $this->multilangs["$obj->lang"]["description"] = $obj->description; + $this->multilangs["$obj->lang"]["other"] = $obj->other; + } + return 1; + } + else + { + $this->error="Error: ".$this->db->lasterror()." - ".$sql; + return -1; + } + } + + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Insert a track that we changed a customer price + * + * @param User $user User making change + * @param int $level price level to change + * @return int <0 if KO, >0 if OK + */ + function _log_price($user,$level=0) + { + // phpcs:enable + global $conf; + + $now=dol_now(); + + // Clean parameters + if (empty($this->price_by_qty)) { $this->price_by_qty=0; + } + + // Add new price + $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_price(price_level,date_price, fk_product, fk_user_author, price, price_ttc, price_base_type,tosell, tva_tx, default_vat_code, recuperableonly,"; + $sql.= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, price_min,price_min_ttc,price_by_qty,entity,fk_price_expression) "; + $sql.= " VALUES(".($level?$level:1).", '".$this->db->idate($now)."',".$this->id.",".$user->id.",".$this->price.",".$this->price_ttc.",'".$this->db->escape($this->price_base_type)."',".$this->status.",".$this->tva_tx.", ".($this->default_vat_code?("'".$this->db->escape($this->default_vat_code)."'"):"null").",".$this->tva_npr.","; + $sql.= " ".$this->localtax1_tx.", ".$this->localtax2_tx.", '".$this->db->escape($this->localtax1_type)."', '".$this->db->escape($this->localtax2_type)."', ".$this->price_min.",".$this->price_min_ttc.",".$this->price_by_qty.",".$conf->entity.",".($this->fk_price_expression > 0?$this->fk_price_expression:'null'); + $sql.= ")"; + + dol_syslog(get_class($this)."::_log_price", LOG_DEBUG); + $resql=$this->db->query($sql); + if(! $resql) { + $this->error=$this->db->lasterror(); + dol_print_error($this->db); + return -1; + } + else + { + return 1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Delete a price line + * + * @param User $user Object user + * @param int $rowid Line id to delete + * @return int <0 if KO, >0 if OK + */ + function log_price_delete($user, $rowid) + { + // phpcs:enable + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_price_by_qty"; + $sql.= " WHERE fk_product_price=".$rowid; + $resql=$this->db->query($sql); + + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_price"; + $sql.= " WHERE rowid=".$rowid; + $resql=$this->db->query($sql); + if ($resql) { + return 1; + } + else + { + $this->error=$this->db->lasterror(); + return -1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Read price used by a provider. + * We enter as input couple prodfournprice/qty or triplet qty/product_id/fourn_ref. + * This also set some properties on product like ->buyprice, ->fourn_pu, ... + * + * @param int $prodfournprice Id du tarif = rowid table product_fournisseur_price + * @param double $qty Quantity asked or -1 to get first entry found + * @param int $product_id Filter on a particular product id + * @param string $fourn_ref Filter on a supplier price ref. 'none' to exclude ref in search. + * @param int $fk_soc If of supplier + * @return int <-1 if KO, -1 if qty not enough, 0 if OK but nothing found, id_product if OK and found. May also initialize some properties like (->ref_supplier, buyprice, fourn_pu, vatrate_supplier...) + */ + function get_buyprice($prodfournprice, $qty, $product_id=0, $fourn_ref='', $fk_soc=0) + { + // phpcs:enable + global $conf; + $result = 0; + + // We do a first seach with a select by searching with couple prodfournprice and qty only (later we will search on triplet qty/product_id/fourn_ref) + $sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.remise_percent,"; + $sql.= " pfp.fk_product, pfp.ref_fourn, pfp.desc_fourn, pfp.fk_soc, pfp.tva_tx, pfp.fk_supplier_price_expression"; + $sql.= " ,pfp.default_vat_code"; + $sql.= " ,pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; + $sql.= " WHERE pfp.rowid = ".$prodfournprice; + if ($qty > 0) { $sql.= " AND pfp.quantity <= ".$qty; + } + $sql.= " ORDER BY pfp.quantity DESC"; + + dol_syslog(get_class($this)."::get_buyprice first search by prodfournprice/qty", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $obj = $this->db->fetch_object($resql); + if ($obj && $obj->quantity > 0) // If we found a supplier prices from the id of supplier price + { + if (!empty($conf->dynamicprices->enabled) && !empty($obj->fk_supplier_price_expression)) { + include_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; $prod_supplier = new ProductFournisseur($this->db); $prod_supplier->product_fourn_price_id = $obj->rowid; $prod_supplier->id = $obj->fk_product; @@ -1588,2181 +1573,2221 @@ class Product extends CommonObject $priceparser = new PriceParser($this->db); $price_result = $priceparser->parseProductSupplier($prod_supplier); if ($price_result >= 0) { - $obj->price = $price_result; + $obj->price = $price_result; } } $this->product_fourn_price_id = $obj->rowid; - $this->buyprice = $obj->price; // deprecated - $this->fourn_pu = $obj->price / $obj->quantity; // Unit price of product of supplier - $this->fourn_price_base_type = 'HT'; // Price base type - $this->fourn_socid = $obj->fk_soc; // Company that offer this price - $this->ref_fourn = $obj->ref_fourn; // deprecated - $this->ref_supplier = $obj->ref_fourn; // Ref supplier - $this->desc_supplier = $obj->desc_fourn; // desc supplier - $this->remise_percent = $obj->remise_percent; // remise percent if present and not typed - $this->vatrate_supplier = $obj->tva_tx; // Vat ref supplier - $this->default_vat_code = $obj->default_vat_code; // Vat code supplier + $this->buyprice = $obj->price; // deprecated + $this->fourn_pu = $obj->price / $obj->quantity; // Unit price of product of supplier + $this->fourn_price_base_type = 'HT'; // Price base type + $this->fourn_socid = $obj->fk_soc; // Company that offer this price + $this->ref_fourn = $obj->ref_fourn; // deprecated + $this->ref_supplier = $obj->ref_fourn; // Ref supplier + $this->desc_supplier = $obj->desc_fourn; // desc supplier + $this->remise_percent = $obj->remise_percent; // remise percent if present and not typed + $this->vatrate_supplier = $obj->tva_tx; // Vat ref supplier + $this->default_vat_code = $obj->default_vat_code; // Vat code supplier $this->fourn_multicurrency_price = $obj->multicurrency_price; $this->fourn_multicurrency_unitprice = $obj->multicurrency_unitprice; $this->fourn_multicurrency_tx = $obj->multicurrency_tx; $this->fourn_multicurrency_id = $obj->fk_multicurrency; $this->fourn_multicurrency_code = $obj->multicurrency_code; - $result=$obj->fk_product; - return $result; - } - else // If not found - { - // We do a second search by doing a select again but searching with less reliable criteria: couple qty/id product, and if set fourn_ref or fk_soc. - $sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.fk_soc,"; - $sql.= " pfp.fk_product, pfp.ref_fourn as ref_supplier, pfp.desc_fourn as desc_supplier, pfp.tva_tx, pfp.fk_supplier_price_expression"; - $sql.= " ,pfp.default_vat_code"; + $result=$obj->fk_product; + return $result; + } + else // If not found + { + // We do a second search by doing a select again but searching with less reliable criteria: couple qty/id product, and if set fourn_ref or fk_soc. + $sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.fk_soc,"; + $sql.= " pfp.fk_product, pfp.ref_fourn as ref_supplier, pfp.desc_fourn as desc_supplier, pfp.tva_tx, pfp.fk_supplier_price_expression"; + $sql.= " ,pfp.default_vat_code"; $sql.= " ,pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; - $sql.= " WHERE pfp.fk_product = ".$product_id; - if ($fourn_ref != 'none') $sql.= " AND pfp.ref_fourn = '".$fourn_ref."'"; - if ($fk_soc > 0) $sql.= " AND pfp.fk_soc = ".$fk_soc; - if ($qty > 0) $sql.= " AND pfp.quantity <= ".$qty; - $sql.= " ORDER BY pfp.quantity DESC"; - $sql.= " LIMIT 1"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; + $sql.= " WHERE pfp.fk_product = ".$product_id; + if ($fourn_ref != 'none') { $sql.= " AND pfp.ref_fourn = '".$fourn_ref."'"; + } + if ($fk_soc > 0) { $sql.= " AND pfp.fk_soc = ".$fk_soc; + } + if ($qty > 0) { $sql.= " AND pfp.quantity <= ".$qty; + } + $sql.= " ORDER BY pfp.quantity DESC"; + $sql.= " LIMIT 1"; - dol_syslog(get_class($this)."::get_buyprice second search from qty/ref/product_id", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - $obj = $this->db->fetch_object($resql); - if ($obj && $obj->quantity > 0) // If found - { - if (!empty($conf->dynamicprices->enabled) && !empty($obj->fk_supplier_price_expression)) - { - require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; - $prod_supplier = new ProductFournisseur($this->db); - $prod_supplier->product_fourn_price_id = $obj->rowid; - $prod_supplier->id = $obj->fk_product; - $prod_supplier->fourn_qty = $obj->quantity; - $prod_supplier->fourn_tva_tx = $obj->tva_tx; - $prod_supplier->fk_supplier_price_expression = $obj->fk_supplier_price_expression; - $priceparser = new PriceParser($this->db); - $price_result = $priceparser->parseProductSupplier($prod_supplier); - if ($result >= 0) { - $obj->price = $price_result; - } - } - $this->product_fourn_price_id = $obj->rowid; - $this->buyprice = $obj->price; // deprecated - $this->fourn_qty = $obj->quantity; // min quantity for price for a virtual supplier - $this->fourn_pu = $obj->price / $obj->quantity; // Unit price of product for a virtual supplier - $this->fourn_price_base_type = 'HT'; // Price base type for a virtual supplier - $this->fourn_socid = $obj->fk_soc; // Company that offer this price - $this->ref_fourn = $obj->ref_supplier; // deprecated - $this->ref_supplier = $obj->ref_supplier; // Ref supplier - $this->desc_supplier = $obj->desc_supplier; // desc supplier - $this->remise_percent = $obj->remise_percent; // remise percent if present and not typed - $this->vatrate_supplier = $obj->tva_tx; // Vat ref supplier - $this->default_vat_code = $obj->default_vat_code; // Vat code supplier + dol_syslog(get_class($this)."::get_buyprice second search from qty/ref/product_id", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $obj = $this->db->fetch_object($resql); + if ($obj && $obj->quantity > 0) // If found + { + if (!empty($conf->dynamicprices->enabled) && !empty($obj->fk_supplier_price_expression)) { + include_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; + $prod_supplier = new ProductFournisseur($this->db); + $prod_supplier->product_fourn_price_id = $obj->rowid; + $prod_supplier->id = $obj->fk_product; + $prod_supplier->fourn_qty = $obj->quantity; + $prod_supplier->fourn_tva_tx = $obj->tva_tx; + $prod_supplier->fk_supplier_price_expression = $obj->fk_supplier_price_expression; + $priceparser = new PriceParser($this->db); + $price_result = $priceparser->parseProductSupplier($prod_supplier); + if ($result >= 0) { + $obj->price = $price_result; + } + } + $this->product_fourn_price_id = $obj->rowid; + $this->buyprice = $obj->price; // deprecated + $this->fourn_qty = $obj->quantity; // min quantity for price for a virtual supplier + $this->fourn_pu = $obj->price / $obj->quantity; // Unit price of product for a virtual supplier + $this->fourn_price_base_type = 'HT'; // Price base type for a virtual supplier + $this->fourn_socid = $obj->fk_soc; // Company that offer this price + $this->ref_fourn = $obj->ref_supplier; // deprecated + $this->ref_supplier = $obj->ref_supplier; // Ref supplier + $this->desc_supplier = $obj->desc_supplier; // desc supplier + $this->remise_percent = $obj->remise_percent; // remise percent if present and not typed + $this->vatrate_supplier = $obj->tva_tx; // Vat ref supplier + $this->default_vat_code = $obj->default_vat_code; // Vat code supplier $this->fourn_multicurrency_price = $obj->multicurrency_price; $this->fourn_multicurrency_unitprice = $obj->multicurrency_unitprice; $this->fourn_multicurrency_tx = $obj->multicurrency_tx; $this->fourn_multicurrency_id = $obj->fk_multicurrency; $this->fourn_multicurrency_code = $obj->multicurrency_code; - $result=$obj->fk_product; - return $result; - } - else - { - return -1; // Ce produit n'existe pas avec cet id tarif fournisseur ou existe mais qte insuffisante, ni pour le couple produit/ref fournisseur dans la quantité. - } - } - else - { - $this->error=$this->db->lasterror(); - return -3; - } - } - } - else - { - $this->error=$this->db->lasterror(); - return -2; - } - } + $result=$obj->fk_product; + return $result; + } + else + { + return -1; // Ce produit n'existe pas avec cet id tarif fournisseur ou existe mais qte insuffisante, ni pour le couple produit/ref fournisseur dans la quantité. + } + } + else + { + $this->error=$this->db->lasterror(); + return -3; + } + } + } + else + { + $this->error=$this->db->lasterror(); + return -2; + } + } - /** - * Modify customer price of a product/Service - * - * @param double $newprice New price - * @param string $newpricebase HT or TTC - * @param User $user Object user that make change - * @param double $newvat New VAT Rate (For example 8.5. Should not be a string) - * @param double $newminprice New price min - * @param int $level 0=standard, >0 = level if multilevel prices - * @param int $newnpr 0=Standard vat rate, 1=Special vat rate for French NPR VAT - * @param int $newpbq 1 if it has price by quantity - * @param int $ignore_autogen Used to avoid infinite loops - * @param array $localtaxes_array Array with localtaxes info array('0'=>type1,'1'=>rate1,'2'=>type2,'3'=>rate2) (loaded by getLocalTaxesFromRate(vatrate, 0, ...) function). - * @param string $newdefaultvatcode Default vat code - * @return int <0 if KO, >0 if OK - */ - function updatePrice($newprice, $newpricebase, $user, $newvat='',$newminprice=0, $level=0, $newnpr=0, $newpbq=0, $ignore_autogen=0, $localtaxes_array=array(), $newdefaultvatcode='') - { - global $conf,$langs; + /** + * Modify customer price of a product/Service + * + * @param double $newprice New price + * @param string $newpricebase HT or TTC + * @param User $user Object user that make change + * @param double $newvat New VAT Rate (For example 8.5. Should not be a string) + * @param double $newminprice New price min + * @param int $level 0=standard, >0 = level if multilevel prices + * @param int $newnpr 0=Standard vat rate, 1=Special vat rate for French NPR VAT + * @param int $newpbq 1 if it has price by quantity + * @param int $ignore_autogen Used to avoid infinite loops + * @param array $localtaxes_array Array with localtaxes info array('0'=>type1,'1'=>rate1,'2'=>type2,'3'=>rate2) (loaded by getLocalTaxesFromRate(vatrate, 0, ...) function). + * @param string $newdefaultvatcode Default vat code + * @return int <0 if KO, >0 if OK + */ + function updatePrice($newprice, $newpricebase, $user, $newvat='',$newminprice=0, $level=0, $newnpr=0, $newpbq=0, $ignore_autogen=0, $localtaxes_array=array(), $newdefaultvatcode='') + { + global $conf,$langs; - $id=$this->id; + $id=$this->id; - dol_syslog(get_class($this)."::update_price id=".$id." newprice=".$newprice." newpricebase=".$newpricebase." newminprice=".$newminprice." level=".$level." npr=".$newnpr." newdefaultvatcode=".$newdefaultvatcode); + dol_syslog(get_class($this)."::update_price id=".$id." newprice=".$newprice." newpricebase=".$newpricebase." newminprice=".$newminprice." level=".$level." npr=".$newnpr." newdefaultvatcode=".$newdefaultvatcode); - // Clean parameters - if (empty($this->tva_tx)) $this->tva_tx=0; - if (empty($newnpr)) $newnpr=0; + // Clean parameters + if (empty($this->tva_tx)) { $this->tva_tx=0; + } + if (empty($newnpr)) { $newnpr=0; + } - // Check parameters - if ($newvat == '') $newvat=$this->tva_tx; + // Check parameters + if ($newvat == '') { $newvat=$this->tva_tx; + } - // If multiprices are enabled, then we check if the current product is subject to price autogeneration - // Price will be modified ONLY when the first one is the one that is being modified - if ((!empty($conf->global->PRODUIT_MULTIPRICES) || ! empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) && !$ignore_autogen && $this->price_autogen && ($level == 1)) - { - return $this->generateMultiprices($user, $newprice, $newpricebase, $newvat, $newnpr, $newpbq); - } + // If multiprices are enabled, then we check if the current product is subject to price autogeneration + // Price will be modified ONLY when the first one is the one that is being modified + if ((!empty($conf->global->PRODUIT_MULTIPRICES) || ! empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) && !$ignore_autogen && $this->price_autogen && ($level == 1)) { + return $this->generateMultiprices($user, $newprice, $newpricebase, $newvat, $newnpr, $newpbq); + } - if (! empty($newminprice) && ($newminprice > $newprice)) - { - $this->error='ErrorPriceCantBeLowerThanMinPrice'; - return -1; - } + if (! empty($newminprice) && ($newminprice > $newprice)) { + $this->error='ErrorPriceCantBeLowerThanMinPrice'; + return -1; + } - if ($newprice !== '' || $newprice === 0) - { - if ($newpricebase == 'TTC') - { - $price_ttc = price2num($newprice,'MU'); - $price = price2num($newprice) / (1 + ($newvat / 100)); - $price = price2num($price,'MU'); + if ($newprice !== '' || $newprice === 0) { + if ($newpricebase == 'TTC') { + $price_ttc = price2num($newprice, 'MU'); + $price = price2num($newprice) / (1 + ($newvat / 100)); + $price = price2num($price, 'MU'); - if ($newminprice != '' || $newminprice == 0) - { - $price_min_ttc = price2num($newminprice,'MU'); - $price_min = price2num($newminprice) / (1 + ($newvat / 100)); - $price_min = price2num($price_min,'MU'); - } - else - { - $price_min=0; - $price_min_ttc=0; - } - } - else - { - $price = price2num($newprice,'MU'); - $price_ttc = ( $newnpr != 1 ) ? price2num($newprice) * (1 + ($newvat / 100)) : $price; - $price_ttc = price2num($price_ttc,'MU'); + if ($newminprice != '' || $newminprice == 0) { + $price_min_ttc = price2num($newminprice, 'MU'); + $price_min = price2num($newminprice) / (1 + ($newvat / 100)); + $price_min = price2num($price_min, 'MU'); + } + else + { + $price_min=0; + $price_min_ttc=0; + } + } + else + { + $price = price2num($newprice, 'MU'); + $price_ttc = ( $newnpr != 1 ) ? price2num($newprice) * (1 + ($newvat / 100)) : $price; + $price_ttc = price2num($price_ttc, 'MU'); - if ( $newminprice !== '' || $newminprice === 0) - { - $price_min = price2num($newminprice,'MU'); - $price_min_ttc = price2num($newminprice) * (1 + ($newvat / 100)); - $price_min_ttc = price2num($price_min_ttc,'MU'); - //print 'X'.$newminprice.'-'.$price_min; - } - else - { - $price_min=0; - $price_min_ttc=0; - } - } - //print 'x'.$id.'-'.$newprice.'-'.$newpricebase.'-'.$price.'-'.$price_ttc.'-'.$price_min.'-'.$price_min_ttc; + if ($newminprice !== '' || $newminprice === 0) { + $price_min = price2num($newminprice, 'MU'); + $price_min_ttc = price2num($newminprice) * (1 + ($newvat / 100)); + $price_min_ttc = price2num($price_min_ttc, 'MU'); + //print 'X'.$newminprice.'-'.$price_min; + } + else + { + $price_min=0; + $price_min_ttc=0; + } + } + //print 'x'.$id.'-'.$newprice.'-'.$newpricebase.'-'.$price.'-'.$price_ttc.'-'.$price_min.'-'.$price_min_ttc; - if (count($localtaxes_array) > 0) - { - $localtaxtype1=$localtaxes_array['0']; - $localtax1=$localtaxes_array['1']; - $localtaxtype2=$localtaxes_array['2']; - $localtax2=$localtaxes_array['3']; - } - else // old method. deprecated because ot can't retreive type - { - $localtaxtype1='0'; - $localtax1=get_localtax($newvat,1); - $localtaxtype2='0'; - $localtax2=get_localtax($newvat,2); - } - if (empty($localtax1)) $localtax1=0; // If = '' then = 0 - if (empty($localtax2)) $localtax2=0; // If = '' then = 0 + if (count($localtaxes_array) > 0) { + $localtaxtype1=$localtaxes_array['0']; + $localtax1=$localtaxes_array['1']; + $localtaxtype2=$localtaxes_array['2']; + $localtax2=$localtaxes_array['3']; + } + else // old method. deprecated because ot can't retreive type + { + $localtaxtype1='0'; + $localtax1=get_localtax($newvat, 1); + $localtaxtype2='0'; + $localtax2=get_localtax($newvat, 2); + } + if (empty($localtax1)) { $localtax1=0; // If = '' then = 0 + } + if (empty($localtax2)) { $localtax2=0; // If = '' then = 0 + } - $this->db->begin(); + $this->db->begin(); - // Ne pas mettre de quote sur les numeriques decimaux. - // Ceci provoque des stockages avec arrondis en base au lieu des valeurs exactes. - $sql = "UPDATE ".MAIN_DB_PREFIX."product SET"; - $sql.= " price_base_type='".$newpricebase."',"; - $sql.= " price=".$price.","; - $sql.= " price_ttc=".$price_ttc.","; - $sql.= " price_min=".$price_min.","; - $sql.= " price_min_ttc=".$price_min_ttc.","; - $sql.= " localtax1_tx=".($localtax1>=0?$localtax1:'NULL').","; - $sql.= " localtax2_tx=".($localtax2>=0?$localtax2:'NULL').","; - $sql.= " localtax1_type=".($localtaxtype1!=''?"'".$localtaxtype1."'":"'0'").","; - $sql.= " localtax2_type=".($localtaxtype2!=''?"'".$localtaxtype2."'":"'0'").","; + // Ne pas mettre de quote sur les numeriques decimaux. + // Ceci provoque des stockages avec arrondis en base au lieu des valeurs exactes. + $sql = "UPDATE ".MAIN_DB_PREFIX."product SET"; + $sql.= " price_base_type='".$newpricebase."',"; + $sql.= " price=".$price.","; + $sql.= " price_ttc=".$price_ttc.","; + $sql.= " price_min=".$price_min.","; + $sql.= " price_min_ttc=".$price_min_ttc.","; + $sql.= " localtax1_tx=".($localtax1>=0?$localtax1:'NULL').","; + $sql.= " localtax2_tx=".($localtax2>=0?$localtax2:'NULL').","; + $sql.= " localtax1_type=".($localtaxtype1!=''?"'".$localtaxtype1."'":"'0'").","; + $sql.= " localtax2_type=".($localtaxtype2!=''?"'".$localtaxtype2."'":"'0'").","; $sql.= " default_vat_code=".($newdefaultvatcode?"'".$this->db->escape($newdefaultvatcode)."'":"null").","; - $sql.= " tva_tx='".price2num($newvat)."',"; + $sql.= " tva_tx='".price2num($newvat)."',"; $sql.= " recuperableonly='".$newnpr."'"; - $sql.= " WHERE rowid = ".$id; + $sql.= " WHERE rowid = ".$id; - dol_syslog(get_class($this)."::update_price", LOG_DEBUG); - $resql=$this->db->query($sql); - if ($resql) - { - $this->multiprices[$level] = $price; - $this->multiprices_ttc[$level] = $price_ttc; - $this->multiprices_min[$level]= $price_min; - $this->multiprices_min_ttc[$level]= $price_min_ttc; - $this->multiprices_base_type[$level]= $newpricebase; - $this->multiprices_default_vat_code[$level]= $newdefaultvatcode; - $this->multiprices_tva_tx[$level]= $newvat; - $this->multiprices_recuperableonly[$level]= $newnpr; + dol_syslog(get_class($this)."::update_price", LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) { + $this->multiprices[$level] = $price; + $this->multiprices_ttc[$level] = $price_ttc; + $this->multiprices_min[$level]= $price_min; + $this->multiprices_min_ttc[$level]= $price_min_ttc; + $this->multiprices_base_type[$level]= $newpricebase; + $this->multiprices_default_vat_code[$level]= $newdefaultvatcode; + $this->multiprices_tva_tx[$level]= $newvat; + $this->multiprices_recuperableonly[$level]= $newnpr; - $this->price = $price; - $this->price_ttc = $price_ttc; - $this->price_min = $price_min; - $this->price_min_ttc = $price_min_ttc; - $this->price_base_type = $newpricebase; - $this->default_vat_code = $newdefaultvatcode; - $this->tva_tx = $newvat; - $this->tva_npr = $newnpr; - //Local taxes - $this->localtax1_tx = $localtax1; - $this->localtax2_tx = $localtax2; - $this->localtax1_type = $localtaxtype1; - $this->localtax2_type = $localtaxtype2; + $this->price = $price; + $this->price_ttc = $price_ttc; + $this->price_min = $price_min; + $this->price_min_ttc = $price_min_ttc; + $this->price_base_type = $newpricebase; + $this->default_vat_code = $newdefaultvatcode; + $this->tva_tx = $newvat; + $this->tva_npr = $newnpr; + //Local taxes + $this->localtax1_tx = $localtax1; + $this->localtax2_tx = $localtax2; + $this->localtax1_type = $localtaxtype1; + $this->localtax2_type = $localtaxtype2; - // Price by quantity - $this->price_by_qty = $newpbq; + // Price by quantity + $this->price_by_qty = $newpbq; - $this->_log_price($user,$level); // Save price for level into table product_price + $this->_log_price($user, $level); // Save price for level into table product_price - $this->level = $level; // Store level of price edited for trigger + $this->level = $level; // Store level of price edited for trigger // Call trigger - $result=$this->call_trigger('PRODUCT_PRICE_MODIFY',$user); - if ($result < 0) - { - $this->db->rollback(); - return -1; + $result=$this->call_trigger('PRODUCT_PRICE_MODIFY', $user); + if ($result < 0) { + $this->db->rollback(); + return -1; } // End call triggers $this->db->commit(); - } - else - { - $this->db->rollback(); - dol_print_error($this->db); - } - } + } + else + { + $this->db->rollback(); + dol_print_error($this->db); + } + } - return 1; - } + return 1; + } /** * Sets the supplier price expression * - * @param int $expression_id Expression - * @return int <0 if KO, >0 if OK - * @deprecated Use Product::update instead + * @param int $expression_id Expression + * @return int <0 if KO, >0 if OK + * @deprecated Use Product::update instead */ function setPriceExpression($expression_id) { - global $user; + global $user; - $this->fk_price_expression = $expression_id; + $this->fk_price_expression = $expression_id; - return $this->update($this->id, $user); + return $this->update($this->id, $user); } - /** - * Load a product in memory from database - * - * @param int $id Id of product/service to load - * @param string $ref Ref of product/service to load - * @param string $ref_ext Ref ext of product/service to load - * @param int $ignore_expression Ignores the math expression for calculating price and uses the db value instead - * @return int <0 if KO, 0 if not found, >0 if OK - */ - function fetch($id='', $ref='', $ref_ext='', $ignore_expression=0) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; + /** + * Load a product in memory from database + * + * @param int $id Id of product/service to load + * @param string $ref Ref of product/service to load + * @param string $ref_ext Ref ext of product/service to load + * @param string $barcode Barcode of product/service to load + * @param int $ignore_expression Ignores the math expression for calculating price and uses the db value instead + * @return int <0 if KO, 0 if not found, >0 if OK + */ + function fetch($id='', $ref='', $ref_ext='', $barcode='', $ignore_expression=0) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; - global $langs, $conf; + global $langs, $conf; - dol_syslog(get_class($this)."::fetch id=".$id." ref=".$ref." ref_ext=".$ref_ext); + dol_syslog(get_class($this)."::fetch id=".$id." ref=".$ref." ref_ext=".$ref_ext); - // Check parameters - if (! $id && ! $ref && ! $ref_ext) - { - $this->error='ErrorWrongParameters'; - dol_syslog(get_class($this)."::fetch ".$this->error); - return -1; - } + // Check parameters + if (! $id && ! $ref && ! $ref_ext && ! $barcode) { + $this->error='ErrorWrongParameters'; + dol_syslog(get_class($this)."::fetch ".$this->error); + return -1; + } - $sql = "SELECT rowid, ref, ref_ext, label, description, url, note as note_private, customcode, fk_country, price, price_ttc,"; - $sql.= " price_min, price_min_ttc, price_base_type, cost_price, default_vat_code, tva_tx, recuperableonly as tva_npr, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, tosell,"; - $sql.= " tobuy, fk_product_type, duration, fk_default_warehouse, seuil_stock_alerte, canvas, weight, weight_units,"; - $sql.= " length, length_units, width, width_units, height, height_units,"; - $sql.= " surface, surface_units, volume, volume_units, barcode, fk_barcode_type, finished,"; - $sql.= " accountancy_code_buy, accountancy_code_sell, accountancy_code_sell_intra, accountancy_code_sell_export, stock, pmp,"; - $sql.= " datec, tms, import_key, entity, desiredstock, tobatch, fk_unit,"; - $sql.= " fk_price_expression, price_autogen"; - $sql.= " FROM ".MAIN_DB_PREFIX."product"; - if ($id) $sql.= " WHERE rowid = ".$this->db->escape($id); - else - { - $sql.= " WHERE entity IN (".getEntity($this->element).")"; - if ($ref) $sql.= " AND ref = '".$this->db->escape($ref)."'"; - else if ($ref_ext) $sql.= " AND ref_ext = '".$this->db->escape($ref_ext)."'"; - } + $sql = "SELECT rowid, ref, ref_ext, label, description, url, note as note_private, customcode, fk_country, price, price_ttc,"; + $sql.= " price_min, price_min_ttc, price_base_type, cost_price, default_vat_code, tva_tx, recuperableonly as tva_npr, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, tosell,"; + $sql.= " tobuy, fk_product_type, duration, fk_default_warehouse, seuil_stock_alerte, canvas, weight, weight_units,"; + $sql.= " length, length_units, width, width_units, height, height_units,"; + $sql.= " surface, surface_units, volume, volume_units, barcode, fk_barcode_type, finished,"; + $sql.= " accountancy_code_buy, accountancy_code_sell, accountancy_code_sell_intra, accountancy_code_sell_export, stock, pmp,"; + $sql.= " datec, tms, import_key, entity, desiredstock, tobatch, fk_unit,"; + $sql.= " fk_price_expression, price_autogen"; + $sql.= " FROM ".MAIN_DB_PREFIX."product"; + if ($id) { $sql.= " WHERE rowid = ".$this->db->escape($id); + } else + { + $sql.= " WHERE entity IN (".getEntity($this->element).")"; + if ($ref) { $sql.= " AND ref = '".$this->db->escape($ref)."'"; + } else if ($ref_ext) { $sql.= " AND ref_ext = '".$this->db->escape($ref_ext)."'"; + } else if ($barcode) { $sql.= " AND barcode = '".$this->db->escape($barcode)."'"; + } + } - $resql = $this->db->query($sql); - if ( $resql ) - { - if ($this->db->num_rows($resql) > 0) - { - $obj = $this->db->fetch_object($resql); + $resql = $this->db->query($sql); + if ($resql ) { + if ($this->db->num_rows($resql) > 0) { + $obj = $this->db->fetch_object($resql); - $this->id = $obj->rowid; - $this->ref = $obj->ref; - $this->ref_ext = $obj->ref_ext; - $this->label = $obj->label; - $this->description = $obj->description; - $this->url = $obj->url; - $this->note_private = $obj->note_private; - $this->note = $obj->note_private; // deprecated + $this->id = $obj->rowid; + $this->ref = $obj->ref; + $this->ref_ext = $obj->ref_ext; + $this->label = $obj->label; + $this->description = $obj->description; + $this->url = $obj->url; + $this->note_private = $obj->note_private; + $this->note = $obj->note_private; // deprecated - $this->type = $obj->fk_product_type; - $this->status = $obj->tosell; - $this->status_buy = $obj->tobuy; - $this->status_batch = $obj->tobatch; + $this->type = $obj->fk_product_type; + $this->status = $obj->tosell; + $this->status_buy = $obj->tobuy; + $this->status_batch = $obj->tobatch; - $this->customcode = $obj->customcode; - $this->country_id = $obj->fk_country; - $this->country_code = getCountry($this->country_id,2,$this->db); - $this->price = $obj->price; - $this->price_ttc = $obj->price_ttc; - $this->price_min = $obj->price_min; - $this->price_min_ttc = $obj->price_min_ttc; - $this->price_base_type = $obj->price_base_type; - $this->cost_price = $obj->cost_price; - $this->default_vat_code = $obj->default_vat_code; - $this->tva_tx = $obj->tva_tx; - //! French VAT NPR - $this->tva_npr = $obj->tva_npr; - $this->recuperableonly = $obj->tva_npr; // For backward compatibility - //! Local taxes - $this->localtax1_tx = $obj->localtax1_tx; - $this->localtax2_tx = $obj->localtax2_tx; - $this->localtax1_type = $obj->localtax1_type; - $this->localtax2_type = $obj->localtax2_type; + $this->customcode = $obj->customcode; + $this->country_id = $obj->fk_country; + $this->country_code = getCountry($this->country_id, 2, $this->db); + $this->price = $obj->price; + $this->price_ttc = $obj->price_ttc; + $this->price_min = $obj->price_min; + $this->price_min_ttc = $obj->price_min_ttc; + $this->price_base_type = $obj->price_base_type; + $this->cost_price = $obj->cost_price; + $this->default_vat_code = $obj->default_vat_code; + $this->tva_tx = $obj->tva_tx; + //! French VAT NPR + $this->tva_npr = $obj->tva_npr; + $this->recuperableonly = $obj->tva_npr; // For backward compatibility + //! Local taxes + $this->localtax1_tx = $obj->localtax1_tx; + $this->localtax2_tx = $obj->localtax2_tx; + $this->localtax1_type = $obj->localtax1_type; + $this->localtax2_type = $obj->localtax2_type; - $this->finished = $obj->finished; - $this->duration = $obj->duration; - $this->duration_value = substr($obj->duration,0,dol_strlen($obj->duration)-1); - $this->duration_unit = substr($obj->duration,-1); - $this->canvas = $obj->canvas; - $this->weight = $obj->weight; - $this->weight_units = $obj->weight_units; - $this->length = $obj->length; - $this->length_units = $obj->length_units; - $this->width = $obj->width; - $this->width_units = $obj->width_units; - $this->height = $obj->height; - $this->height_units = $obj->height_units; + $this->finished = $obj->finished; + $this->duration = $obj->duration; + $this->duration_value = substr($obj->duration, 0, dol_strlen($obj->duration)-1); + $this->duration_unit = substr($obj->duration, -1); + $this->canvas = $obj->canvas; + $this->weight = $obj->weight; + $this->weight_units = $obj->weight_units; + $this->length = $obj->length; + $this->length_units = $obj->length_units; + $this->width = $obj->width; + $this->width_units = $obj->width_units; + $this->height = $obj->height; + $this->height_units = $obj->height_units; - $this->surface = $obj->surface; - $this->surface_units = $obj->surface_units; - $this->volume = $obj->volume; - $this->volume_units = $obj->volume_units; - $this->barcode = $obj->barcode; - $this->barcode_type = $obj->fk_barcode_type; + $this->surface = $obj->surface; + $this->surface_units = $obj->surface_units; + $this->volume = $obj->volume; + $this->volume_units = $obj->volume_units; + $this->barcode = $obj->barcode; + $this->barcode_type = $obj->fk_barcode_type; - $this->accountancy_code_buy = $obj->accountancy_code_buy; - $this->accountancy_code_sell = $obj->accountancy_code_sell; - $this->accountancy_code_sell_intra = $obj->accountancy_code_sell_intra; - $this->accountancy_code_sell_export = $obj->accountancy_code_sell_export; + $this->accountancy_code_buy = $obj->accountancy_code_buy; + $this->accountancy_code_sell = $obj->accountancy_code_sell; + $this->accountancy_code_sell_intra = $obj->accountancy_code_sell_intra; + $this->accountancy_code_sell_export = $obj->accountancy_code_sell_export; - $this->fk_default_warehouse = $obj->fk_default_warehouse; - $this->seuil_stock_alerte = $obj->seuil_stock_alerte; - $this->desiredstock = $obj->desiredstock; - $this->stock_reel = $obj->stock; - $this->pmp = $obj->pmp; + $this->fk_default_warehouse = $obj->fk_default_warehouse; + $this->seuil_stock_alerte = $obj->seuil_stock_alerte; + $this->desiredstock = $obj->desiredstock; + $this->stock_reel = $obj->stock; + $this->pmp = $obj->pmp; - $this->date_creation = $obj->datec; - $this->date_modification = $obj->tms; - $this->import_key = $obj->import_key; - $this->entity = $obj->entity; + $this->date_creation = $obj->datec; + $this->date_modification = $obj->tms; + $this->import_key = $obj->import_key; + $this->entity = $obj->entity; - $this->ref_ext = $obj->ref_ext; - $this->fk_price_expression = $obj->fk_price_expression; - $this->fk_unit = $obj->fk_unit; - $this->price_autogen = $obj->price_autogen; + $this->ref_ext = $obj->ref_ext; + $this->fk_price_expression = $obj->fk_price_expression; + $this->fk_unit = $obj->fk_unit; + $this->price_autogen = $obj->price_autogen; - $this->db->free($resql); + $this->db->free($resql); - // Retreive all extrafield - // fetch optionals attributes and labels - $this->fetch_optionals(); + // Retreive all extrafield + // fetch optionals attributes and labels + $this->fetch_optionals(); - // multilangs - if (! empty($conf->global->MAIN_MULTILANGS)) $this->getMultiLangs(); + // multilangs + if (! empty($conf->global->MAIN_MULTILANGS)) { $this->getMultiLangs(); + } - // Load multiprices array - if (! empty($conf->global->PRODUIT_MULTIPRICES)) // prices per segment - { - for ($i=1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) - { - $sql = "SELECT price, price_ttc, price_min, price_min_ttc,"; - $sql.= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid, recuperableonly"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_price"; - $sql.= " WHERE entity IN (".getEntity('productprice').")"; - $sql.= " AND price_level=".$i; - $sql.= " AND fk_product = ".$this->id; - $sql.= " ORDER BY date_price DESC, rowid DESC"; - $sql.= " LIMIT 1"; - $resql = $this->db->query($sql); - if ($resql) - { - $result = $this->db->fetch_array($resql); - - $this->multiprices[$i]=$result["price"]; - $this->multiprices_ttc[$i]=$result["price_ttc"]; - $this->multiprices_min[$i]=$result["price_min"]; - $this->multiprices_min_ttc[$i]=$result["price_min_ttc"]; - $this->multiprices_base_type[$i]=$result["price_base_type"]; - // Next two fields are used only if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on - $this->multiprices_tva_tx[$i]=$result["tva_tx"]; // TODO Add ' ('.$result['default_vat_code'].')' - $this->multiprices_recuperableonly[$i]=$result["recuperableonly"]; - - // Price by quantity - /* - $this->prices_by_qty[$i]=$result["price_by_qty"]; - $this->prices_by_qty_id[$i]=$result["rowid"]; - // Récuperation de la liste des prix selon qty si flag positionné - if ($this->prices_by_qty[$i] == 1) - { - $sql = "SELECT rowid, price, unitprice, quantity, remise_percent, remise, price_base_type"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_price_by_qty"; - $sql.= " WHERE fk_product_price = ".$this->prices_by_qty_id[$i]; - $sql.= " ORDER BY quantity ASC"; - $resultat=array(); - $resql = $this->db->query($sql); - if ($resql) - { - $ii=0; - while ($result= $this->db->fetch_array($resql)) { - $resultat[$ii]=array(); - $resultat[$ii]["rowid"]=$result["rowid"]; - $resultat[$ii]["price"]= $result["price"]; - $resultat[$ii]["unitprice"]= $result["unitprice"]; - $resultat[$ii]["quantity"]= $result["quantity"]; - $resultat[$ii]["remise_percent"]= $result["remise_percent"]; - $resultat[$ii]["remise"]= $result["remise"]; // deprecated - $resultat[$ii]["price_base_type"]= $result["price_base_type"]; - $ii++; - } - $this->prices_by_qty_list[$i]=$resultat; - } - else - { - dol_print_error($this->db); - return -1; - } - }*/ - } - else - { - dol_print_error($this->db); - return -1; - } - } - } - elseif (! empty($conf->global->PRODUIT_CUSTOMER_PRICES)) // prices per customers - { - // Nothing loaded by default. List may be very long. - } - else if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY)) // prices per quantity - { - $sql = "SELECT price, price_ttc, price_min, price_min_ttc,"; - $sql.= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_price"; - $sql.= " WHERE fk_product = ".$this->id; - $sql.= " ORDER BY date_price DESC, rowid DESC"; - $sql.= " LIMIT 1"; - $resql = $this->db->query($sql); - if ($resql) - { - $result = $this->db->fetch_array($resql); - - // Price by quantity - $this->prices_by_qty[0]=$result["price_by_qty"]; - $this->prices_by_qty_id[0]=$result["rowid"]; - // Récuperation de la liste des prix selon qty si flag positionné - if ($this->prices_by_qty[0] == 1) - { - $sql = "SELECT rowid,price, unitprice, quantity, remise_percent, remise, remise, price_base_type"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_price_by_qty"; - $sql.= " WHERE fk_product_price = ".$this->prices_by_qty_id[0]; - $sql.= " ORDER BY quantity ASC"; - $resultat=array(); - $resql = $this->db->query($sql); - if ($resql) - { - $ii=0; - while ($result= $this->db->fetch_array($resql)) { - $resultat[$ii]=array(); - $resultat[$ii]["rowid"]=$result["rowid"]; - $resultat[$ii]["price"]= $result["price"]; - $resultat[$ii]["unitprice"]= $result["unitprice"]; - $resultat[$ii]["quantity"]= $result["quantity"]; - $resultat[$ii]["remise_percent"]= $result["remise_percent"]; - //$resultat[$ii]["remise"]= $result["remise"]; // deprecated - $resultat[$ii]["price_base_type"]= $result["price_base_type"]; - $ii++; - } - $this->prices_by_qty_list[0]=$resultat; - } - else - { - dol_print_error($this->db); - return -1; - } - } - } - else - { - dol_print_error($this->db); - return -1; - } - } - else if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) // prices per customer and quantity - { - for ($i=1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) - { - $sql = "SELECT price, price_ttc, price_min, price_min_ttc,"; - $sql.= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid, recuperableonly"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_price"; - $sql.= " WHERE entity IN (".getEntity('productprice').")"; - $sql.= " AND price_level=".$i; - $sql.= " AND fk_product = ".$this->id; - $sql.= " ORDER BY date_price DESC, rowid DESC"; - $sql.= " LIMIT 1"; - $resql = $this->db->query($sql); - if ($resql) - { - $result = $this->db->fetch_array($resql); - - $this->multiprices[$i]=$result["price"]; - $this->multiprices_ttc[$i]=$result["price_ttc"]; - $this->multiprices_min[$i]=$result["price_min"]; - $this->multiprices_min_ttc[$i]=$result["price_min_ttc"]; - $this->multiprices_base_type[$i]=$result["price_base_type"]; - // Next two fields are used only if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on - $this->multiprices_tva_tx[$i]=$result["tva_tx"]; // TODO Add ' ('.$result['default_vat_code'].')' - $this->multiprices_recuperableonly[$i]=$result["recuperableonly"]; - - // Price by quantity - $this->prices_by_qty[$i]=$result["price_by_qty"]; - $this->prices_by_qty_id[$i]=$result["rowid"]; - // Récuperation de la liste des prix selon qty si flag positionné - if ($this->prices_by_qty[$i] == 1) - { - $sql = "SELECT rowid, price, unitprice, quantity, remise_percent, remise, price_base_type"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_price_by_qty"; - $sql.= " WHERE fk_product_price = ".$this->prices_by_qty_id[$i]; - $sql.= " ORDER BY quantity ASC"; - $resultat=array(); - $resql = $this->db->query($sql); - if ($resql) - { - $ii=0; - while ($result= $this->db->fetch_array($resql)) { - $resultat[$ii]=array(); - $resultat[$ii]["rowid"]=$result["rowid"]; - $resultat[$ii]["price"]= $result["price"]; - $resultat[$ii]["unitprice"]= $result["unitprice"]; - $resultat[$ii]["quantity"]= $result["quantity"]; - $resultat[$ii]["remise_percent"]= $result["remise_percent"]; - $resultat[$ii]["remise"]= $result["remise"]; // deprecated - $resultat[$ii]["price_base_type"]= $result["price_base_type"]; - $ii++; - } - $this->prices_by_qty_list[$i]=$resultat; - } - else - { - dol_print_error($this->db); - return -1; - } - } - } - else - { - dol_print_error($this->db); - return -1; - } - } - } - - if (!empty($conf->dynamicprices->enabled) && !empty($this->fk_price_expression) && empty($ignore_expression)) + // Load multiprices array + if (! empty($conf->global->PRODUIT_MULTIPRICES)) // prices per segment { - require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; - $priceparser = new PriceParser($this->db); - $price_result = $priceparser->parseProduct($this); - if ($price_result >= 0) + for ($i=1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) { - $this->price = $price_result; - // Calculate the VAT - $this->price_ttc = price2num($this->price) * (1 + ($this->tva_tx / 100)); - $this->price_ttc = price2num($this->price_ttc,'MU'); + $sql = "SELECT price, price_ttc, price_min, price_min_ttc,"; + $sql.= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid, recuperableonly"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_price"; + $sql.= " WHERE entity IN (".getEntity('productprice').")"; + $sql.= " AND price_level=".$i; + $sql.= " AND fk_product = ".$this->id; + $sql.= " ORDER BY date_price DESC, rowid DESC"; + $sql.= " LIMIT 1"; + $resql = $this->db->query($sql); + if ($resql) { + $result = $this->db->fetch_array($resql); + + $this->multiprices[$i]=$result["price"]; + $this->multiprices_ttc[$i]=$result["price_ttc"]; + $this->multiprices_min[$i]=$result["price_min"]; + $this->multiprices_min_ttc[$i]=$result["price_min_ttc"]; + $this->multiprices_base_type[$i]=$result["price_base_type"]; + // Next two fields are used only if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on + $this->multiprices_tva_tx[$i]=$result["tva_tx"]; // TODO Add ' ('.$result['default_vat_code'].')' + $this->multiprices_recuperableonly[$i]=$result["recuperableonly"]; + + // Price by quantity + /* + $this->prices_by_qty[$i]=$result["price_by_qty"]; + $this->prices_by_qty_id[$i]=$result["rowid"]; + // Récuperation de la liste des prix selon qty si flag positionné + if ($this->prices_by_qty[$i] == 1) + { + $sql = "SELECT rowid, price, unitprice, quantity, remise_percent, remise, price_base_type"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_price_by_qty"; + $sql.= " WHERE fk_product_price = ".$this->prices_by_qty_id[$i]; + $sql.= " ORDER BY quantity ASC"; + $resultat=array(); + $resql = $this->db->query($sql); + if ($resql) + { + $ii=0; + while ($result= $this->db->fetch_array($resql)) { + $resultat[$ii]=array(); + $resultat[$ii]["rowid"]=$result["rowid"]; + $resultat[$ii]["price"]= $result["price"]; + $resultat[$ii]["unitprice"]= $result["unitprice"]; + $resultat[$ii]["quantity"]= $result["quantity"]; + $resultat[$ii]["remise_percent"]= $result["remise_percent"]; + $resultat[$ii]["remise"]= $result["remise"]; // deprecated + $resultat[$ii]["price_base_type"]= $result["price_base_type"]; + $ii++; + } + $this->prices_by_qty_list[$i]=$resultat; + } + else + { + dol_print_error($this->db); + return -1; + } + }*/ + } + else + { + dol_print_error($this->db); + return -1; + } + } + } + elseif (! empty($conf->global->PRODUIT_CUSTOMER_PRICES)) // prices per customers + { + // Nothing loaded by default. List may be very long. + } + else if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY)) // prices per quantity + { + $sql = "SELECT price, price_ttc, price_min, price_min_ttc,"; + $sql.= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_price"; + $sql.= " WHERE fk_product = ".$this->id; + $sql.= " ORDER BY date_price DESC, rowid DESC"; + $sql.= " LIMIT 1"; + $resql = $this->db->query($sql); + if ($resql) { + $result = $this->db->fetch_array($resql); + + // Price by quantity + $this->prices_by_qty[0]=$result["price_by_qty"]; + $this->prices_by_qty_id[0]=$result["rowid"]; + // Récuperation de la liste des prix selon qty si flag positionné + if ($this->prices_by_qty[0] == 1) { + $sql = "SELECT rowid,price, unitprice, quantity, remise_percent, remise, remise, price_base_type"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_price_by_qty"; + $sql.= " WHERE fk_product_price = ".$this->prices_by_qty_id[0]; + $sql.= " ORDER BY quantity ASC"; + $resultat=array(); + $resql = $this->db->query($sql); + if ($resql) { + $ii=0; + while ($result= $this->db->fetch_array($resql)) { + $resultat[$ii]=array(); + $resultat[$ii]["rowid"]=$result["rowid"]; + $resultat[$ii]["price"]= $result["price"]; + $resultat[$ii]["unitprice"]= $result["unitprice"]; + $resultat[$ii]["quantity"]= $result["quantity"]; + $resultat[$ii]["remise_percent"]= $result["remise_percent"]; + //$resultat[$ii]["remise"]= $result["remise"]; // deprecated + $resultat[$ii]["price_base_type"]= $result["price_base_type"]; + $ii++; + } + $this->prices_by_qty_list[0]=$resultat; + } + else + { + dol_print_error($this->db); + return -1; + } + } + } + else + { + dol_print_error($this->db); + return -1; + } + } + else if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) // prices per customer and quantity + { + for ($i=1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) + { + $sql = "SELECT price, price_ttc, price_min, price_min_ttc,"; + $sql.= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid, recuperableonly"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_price"; + $sql.= " WHERE entity IN (".getEntity('productprice').")"; + $sql.= " AND price_level=".$i; + $sql.= " AND fk_product = ".$this->id; + $sql.= " ORDER BY date_price DESC, rowid DESC"; + $sql.= " LIMIT 1"; + $resql = $this->db->query($sql); + if ($resql) { + $result = $this->db->fetch_array($resql); + + $this->multiprices[$i]=$result["price"]; + $this->multiprices_ttc[$i]=$result["price_ttc"]; + $this->multiprices_min[$i]=$result["price_min"]; + $this->multiprices_min_ttc[$i]=$result["price_min_ttc"]; + $this->multiprices_base_type[$i]=$result["price_base_type"]; + // Next two fields are used only if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on + $this->multiprices_tva_tx[$i]=$result["tva_tx"]; // TODO Add ' ('.$result['default_vat_code'].')' + $this->multiprices_recuperableonly[$i]=$result["recuperableonly"]; + + // Price by quantity + $this->prices_by_qty[$i]=$result["price_by_qty"]; + $this->prices_by_qty_id[$i]=$result["rowid"]; + // Récuperation de la liste des prix selon qty si flag positionné + if ($this->prices_by_qty[$i] == 1) { + $sql = "SELECT rowid, price, unitprice, quantity, remise_percent, remise, price_base_type"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_price_by_qty"; + $sql.= " WHERE fk_product_price = ".$this->prices_by_qty_id[$i]; + $sql.= " ORDER BY quantity ASC"; + $resultat=array(); + $resql = $this->db->query($sql); + if ($resql) { + $ii=0; + while ($result= $this->db->fetch_array($resql)) { + $resultat[$ii]=array(); + $resultat[$ii]["rowid"]=$result["rowid"]; + $resultat[$ii]["price"]= $result["price"]; + $resultat[$ii]["unitprice"]= $result["unitprice"]; + $resultat[$ii]["quantity"]= $result["quantity"]; + $resultat[$ii]["remise_percent"]= $result["remise_percent"]; + $resultat[$ii]["remise"]= $result["remise"]; // deprecated + $resultat[$ii]["price_base_type"]= $result["price_base_type"]; + $ii++; + } + $this->prices_by_qty_list[$i]=$resultat; + } + else + { + dol_print_error($this->db); + return -1; + } + } + } + else + { + dol_print_error($this->db); + return -1; + } } } - // We should not load stock during the fetch. If someone need stock of product, he must call load_stock after fetching product. - // Instead we just init the stock_warehouse array - $this->stock_warehouse = array(); - - return 1; - } - else - { - return 0; - } - } - else - { - dol_print_error($this->db); - return -1; - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats propale pour le produit/service - * - * @param int $socid Id societe - * @return array Tableau des stats - */ - function load_stats_propale($socid=0) - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT COUNT(DISTINCT p.fk_soc) as nb_customers, COUNT(DISTINCT p.rowid) as nb,"; - $sql.= " COUNT(pd.rowid) as nb_rows, SUM(pd.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."propaldet as pd"; - $sql.= ", ".MAIN_DB_PREFIX."propal as p"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE p.rowid = pd.fk_propal"; - $sql.= " AND p.fk_soc = s.rowid"; - $sql.= " AND p.entity IN (".getEntity('propal').")"; - $sql.= " AND pd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid) $sql .= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - //$sql.= " AND pr.fk_statut != 0"; - if ($socid > 0) $sql.= " AND p.fk_soc = ".$socid; - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_propale['customers']=$obj->nb_customers; - $this->stats_propale['nb']=$obj->nb; - $this->stats_propale['rows']=$obj->nb_rows; - $this->stats_propale['qty']=$obj->qty?$obj->qty:0; - return 1; - } - else - { - $this->error=$this->db->error(); - return -1; - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats propale pour le produit/service - * - * @param int $socid Id thirdparty - * @return array Tableau des stats - */ - function load_stats_proposal_supplier($socid=0) - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT COUNT(DISTINCT p.fk_soc) as nb_suppliers, COUNT(DISTINCT p.rowid) as nb,"; - $sql.= " COUNT(pd.rowid) as nb_rows, SUM(pd.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as pd"; - $sql.= ", ".MAIN_DB_PREFIX."supplier_proposal as p"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE p.rowid = pd.fk_supplier_proposal"; - $sql.= " AND p.fk_soc = s.rowid"; - $sql.= " AND p.entity IN (".getEntity('supplier_proposal').")"; - $sql.= " AND pd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid) $sql .= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - //$sql.= " AND pr.fk_statut != 0"; - if ($socid > 0) $sql.= " AND p.fk_soc = ".$socid; - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_proposal_supplier['suppliers']=$obj->nb_suppliers; - $this->stats_proposal_supplier['nb']=$obj->nb; - $this->stats_proposal_supplier['rows']=$obj->nb_rows; - $this->stats_proposal_supplier['qty']=$obj->qty?$obj->qty:0; - return 1; - } - else - { - $this->error=$this->db->error(); - return -1; - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats commande client pour le produit/service - * - * @param int $socid Id societe pour filtrer sur une societe - * @param string $filtrestatut Id statut pour filtrer sur un statut - * @param int $forVirtualStock Ignore rights filter for virtual stock calculation. - * @return array Array of stats (nb=nb of order, qty=qty ordered) - */ - function load_stats_commande($socid=0,$filtrestatut='', $forVirtualStock = 0) - { - // phpcs:enable - global $conf,$user; - - $sql = "SELECT COUNT(DISTINCT c.fk_soc) as nb_customers, COUNT(DISTINCT c.rowid) as nb,"; - $sql.= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd"; - $sql.= ", ".MAIN_DB_PREFIX."commande as c"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE c.rowid = cd.fk_commande"; - $sql.= " AND c.fk_soc = s.rowid"; - $sql.= " AND c.entity IN (".getEntity('commande').")"; - $sql.= " AND cd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND c.fk_soc = ".$socid; - if ($filtrestatut <> '') $sql.= " AND c.fk_statut in (".$filtrestatut.")"; - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_commande['customers']=$obj->nb_customers; - $this->stats_commande['nb']=$obj->nb; - $this->stats_commande['rows']=$obj->nb_rows; - $this->stats_commande['qty']=$obj->qty?$obj->qty:0; - - // if it's a virtual product, maybe it is in order by extension - if (! empty($conf->global->ORDER_ADD_ORDERS_WITH_PARENT_PROD_IF_INCDEC)) - { - $TFather = $this->getFather(); - if (is_array($TFather) && !empty($TFather)) { - foreach($TFather as &$fatherData) { - $pFather = new Product($this->db); - $pFather->id = $fatherData['id']; - $qtyCoef = $fatherData['qty']; - - if ($fatherData['incdec']) { - $pFather->load_stats_commande($socid, $filtrestatut); - - $this->stats_commande['customers']+=$pFather->stats_commande['customers']; - $this->stats_commande['nb']+=$pFather->stats_commande['nb']; - $this->stats_commande['rows']+=$pFather->stats_commande['rows']; - $this->stats_commande['qty']+=$pFather->stats_commande['qty'] * $qtyCoef; - } - } - } - } - - // If stock decrease is on invoice validation, the theorical stock continue to - // count the orders to ship in theorical stock when some are already removed b invoice validation. - // If option DECREASE_ONLY_UNINVOICEDPRODUCTS is on, we make a compensation. - if (! empty($conf->global->STOCK_CALCULATE_ON_BILL)) - { - if (! empty($conf->global->DECREASE_ONLY_UNINVOICEDPRODUCTS)) - { - $adeduire = 0; - $sql = "SELECT sum(fd.qty) as count FROM ".MAIN_DB_PREFIX."facturedet fd "; - $sql .= " JOIN ".MAIN_DB_PREFIX."facture f ON fd.fk_facture = f.rowid "; - $sql .= " JOIN ".MAIN_DB_PREFIX."element_element el ON el.fk_target = f.rowid and el.targettype = 'facture' and sourcetype = 'commande'"; - $sql .= " JOIN ".MAIN_DB_PREFIX."commande c ON el.fk_source = c.rowid "; - $sql .= " WHERE c.fk_statut IN (".$filtrestatut.") AND c.facture = 0 AND fd.fk_product = ".$this->id; - dol_syslog(__METHOD__.":: sql $sql", LOG_NOTICE); - - $resql = $this->db->query($sql); - if ( $resql ) - { - if ($this->db->num_rows($resql) > 0) - { - $obj = $this->db->fetch_object($resql); - $adeduire += $obj->count; - } - } - - $this->stats_commande['qty'] -= $adeduire; - } - } - - return 1; - } - else - { - $this->error=$this->db->error(); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats commande fournisseur pour le produit/service - * - * @param int $socid Id societe pour filtrer sur une societe - * @param string $filtrestatut Id des statuts pour filtrer sur des statuts - * @param int $forVirtualStock Ignore rights filter for virtual stock calculation. - * @return array Tableau des stats - */ - function load_stats_commande_fournisseur($socid=0,$filtrestatut='', $forVirtualStock = 0) - { - // phpcs:enable - global $conf,$user; - - $sql = "SELECT COUNT(DISTINCT c.fk_soc) as nb_suppliers, COUNT(DISTINCT c.rowid) as nb,"; - $sql.= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd"; - $sql.= ", ".MAIN_DB_PREFIX."commande_fournisseur as c"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE c.rowid = cd.fk_commande"; - $sql.= " AND c.fk_soc = s.rowid"; - $sql.= " AND c.entity IN (".getEntity('supplier_order').")"; - $sql.= " AND cd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND c.fk_soc = ".$socid; - if ($filtrestatut != '') $sql.= " AND c.fk_statut in (".$filtrestatut.")"; // Peut valoir 0 - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_commande_fournisseur['suppliers']=$obj->nb_suppliers; - $this->stats_commande_fournisseur['nb']=$obj->nb; - $this->stats_commande_fournisseur['rows']=$obj->nb_rows; - $this->stats_commande_fournisseur['qty']=$obj->qty?$obj->qty:0; - return 1; - } - else - { - $this->error=$this->db->error().' sql='.$sql; - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats expedition client pour le produit/service - * - * @param int $socid Id societe pour filtrer sur une societe - * @param string $filtrestatut Id statut pour filtrer sur un statut - * @param int $forVirtualStock Ignore rights filter for virtual stock calculation. - * @return array Tableau des stats - */ - function load_stats_sending($socid=0,$filtrestatut='', $forVirtualStock = 0) - { - // phpcs:enable - global $conf,$user; - - $sql = "SELECT COUNT(DISTINCT e.fk_soc) as nb_customers, COUNT(DISTINCT e.rowid) as nb,"; - $sql.= " COUNT(ed.rowid) as nb_rows, SUM(ed.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed"; - $sql.= ", ".MAIN_DB_PREFIX."commandedet as cd"; - $sql.= ", ".MAIN_DB_PREFIX."commande as c"; - $sql.= ", ".MAIN_DB_PREFIX."expedition as e"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE e.rowid = ed.fk_expedition"; - $sql.= " AND c.rowid = cd.fk_commande"; - $sql.= " AND e.fk_soc = s.rowid"; - $sql.= " AND e.entity IN (".getEntity('expedition').")"; - $sql.= " AND ed.fk_origin_line = cd.rowid"; - $sql.= " AND cd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) $sql.= " AND e.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND e.fk_soc = ".$socid; - if ($filtrestatut <> '') $sql.= " AND c.fk_statut in (".$filtrestatut.")"; - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_expedition['customers']=$obj->nb_customers; - $this->stats_expedition['nb']=$obj->nb; - $this->stats_expedition['rows']=$obj->nb_rows; - $this->stats_expedition['qty']=$obj->qty?$obj->qty:0; - return 1; - } - else - { - $this->error=$this->db->error(); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats réception fournisseur pour le produit/service - * - * @param int $socid Id societe pour filtrer sur une societe - * @param string $filtrestatut Id statut pour filtrer sur un statut - * @param int $forVirtualStock Ignore rights filter for virtual stock calculation. - * @return array Tableau des stats - */ - function load_stats_reception($socid=0,$filtrestatut='', $forVirtualStock = 0) - { - // phpcs:enable - global $conf,$user; - - $sql = "SELECT COUNT(DISTINCT cf.fk_soc) as nb_customers, COUNT(DISTINCT cf.rowid) as nb,"; - $sql.= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as fd"; - $sql.= ", ".MAIN_DB_PREFIX."commande_fournisseur as cf"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE cf.rowid = fd.fk_commande"; - $sql.= " AND cf.fk_soc = s.rowid"; - $sql.= " AND cf.entity IN (".getEntity('supplier_order').")"; - $sql.= " AND fd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) $sql.= " AND cf.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND cf.fk_soc = ".$socid; - if ($filtrestatut <> '') $sql.= " AND cf.fk_statut in (".$filtrestatut.")"; - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_reception['suppliers']=$obj->nb_customers; - $this->stats_reception['nb']=$obj->nb; - $this->stats_reception['rows']=$obj->nb_rows; - $this->stats_reception['qty']=$obj->qty?$obj->qty:0; - return 1; - } - else - { - $this->error=$this->db->error(); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats contrat pour le produit/service - * - * @param int $socid Id societe - * @return array Tableau des stats - */ - function load_stats_contrat($socid=0) - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT COUNT(DISTINCT c.fk_soc) as nb_customers, COUNT(DISTINCT c.rowid) as nb,"; - $sql.= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."contratdet as cd"; - $sql.= ", ".MAIN_DB_PREFIX."contrat as c"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE c.rowid = cd.fk_contrat"; - $sql.= " AND c.fk_soc = s.rowid"; - $sql.= " AND c.entity IN (".getEntity('contract').")"; - $sql.= " AND cd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - //$sql.= " AND c.statut != 0"; - if ($socid > 0) $sql.= " AND c.fk_soc = ".$socid; - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_contrat['customers']=$obj->nb_customers; - $this->stats_contrat['nb']=$obj->nb; - $this->stats_contrat['rows']=$obj->nb_rows; - $this->stats_contrat['qty']=$obj->qty?$obj->qty:0; - return 1; - } - else - { - $this->error=$this->db->error().' sql='.$sql; - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats facture pour le produit/service - * - * @param int $socid Id societe - * @return array Tableau des stats - */ - function load_stats_facture($socid=0) - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT COUNT(DISTINCT f.fk_soc) as nb_customers, COUNT(DISTINCT f.rowid) as nb,"; - $sql.= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."facturedet as fd"; - $sql.= ", ".MAIN_DB_PREFIX."facture as f"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE f.rowid = fd.fk_facture"; - $sql.= " AND f.fk_soc = s.rowid"; - $sql.= " AND f.entity IN (".getEntity('facture').")"; - $sql.= " AND fd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - //$sql.= " AND f.fk_statut != 0"; - if ($socid > 0) $sql .= " AND f.fk_soc = ".$socid; - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_facture['customers']=$obj->nb_customers; - $this->stats_facture['nb']=$obj->nb; - $this->stats_facture['rows']=$obj->nb_rows; - $this->stats_facture['qty']=$obj->qty?$obj->qty:0; - return 1; - } - else - { - $this->error=$this->db->error(); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Charge tableau des stats facture pour le produit/service - * - * @param int $socid Id societe - * @return array Tableau des stats - */ - function load_stats_facture_fournisseur($socid=0) - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT COUNT(DISTINCT f.fk_soc) as nb_suppliers, COUNT(DISTINCT f.rowid) as nb,"; - $sql.= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."facture_fourn_det as fd"; - $sql.= ", ".MAIN_DB_PREFIX."facture_fourn as f"; - $sql.= ", ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE f.rowid = fd.fk_facture_fourn"; - $sql.= " AND f.fk_soc = s.rowid"; - $sql.= " AND f.entity IN (".getEntity('facture_fourn').")"; - $sql.= " AND fd.fk_product = ".$this->id; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - //$sql.= " AND f.fk_statut != 0"; - if ($socid > 0) $sql .= " AND f.fk_soc = ".$socid; - - $result = $this->db->query($sql); - if ( $result ) - { - $obj=$this->db->fetch_object($result); - $this->stats_facture_fournisseur['suppliers']=$obj->nb_suppliers; - $this->stats_facture_fournisseur['nb']=$obj->nb; - $this->stats_facture_fournisseur['rows']=$obj->nb_rows; - $this->stats_facture_fournisseur['qty']=$obj->qty?$obj->qty:0; - return 1; - } - else - { - $this->error=$this->db->error(); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return an array formated for showing graphs - * - * @param string $sql Request to execute - * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities - * @param int $year Year (0=current year) - * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 - */ - function _get_stats($sql, $mode, $year=0) - { - // phpcs:enable - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num) - { - $arr = $this->db->fetch_array($resql); - if ($mode == 'byunit') $tab[$arr[1]] = $arr[0]; // 1st field - if ($mode == 'bynumber') $tab[$arr[1]] = $arr[2]; // 3rd field - $i++; - } - } - else - { - $this->error=$this->db->error().' sql='.$sql; - return -1; - } - - if (empty($year)) - { - $year = strftime('%Y',time()); - $month = strftime('%m',time()); - } - else - { - $month=12; // We imagine we are at end of year, so we get last 12 month before, so all correct year. - } - $result = array(); - - for ($j = 0 ; $j < 12 ; $j++) - { - $idx=ucfirst(dol_trunc(dol_print_date(dol_mktime(12,0,0,$month,1,$year),"%b"),3,'right','UTF-8',1)); - $monthnum=sprintf("%02s",$month); - - $result[$j] = array($idx,isset($tab[$year.$month])?$tab[$year.$month]:0); - // $result[$j] = array($monthnum,isset($tab[$year.$month])?$tab[$year.$month]:0); - - $month = "0".($month - 1); - if (dol_strlen($month) == 3) - { - $month = substr($month,1); - } - if ($month == 0) - { - $month = 12; - $year = $year - 1; - } - } - - return array_reverse($result); - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return nb of units or customers invoices in which product is included - * - * @param int $socid Limit count on a particular third party id - * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities - * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only - * @param int $year Year (0=last 12 month) - * @param string $morefilter More sql filters - * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 - */ - function get_nb_vente($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT sum(d.qty), date_format(f.datef, '%Y%m')"; - if ($mode == 'bynumber') $sql.= ", count(DISTINCT f.rowid)"; - $sql.= " FROM ".MAIN_DB_PREFIX."facturedet as d, ".MAIN_DB_PREFIX."facture as f, ".MAIN_DB_PREFIX."societe as s"; - if ($filteronproducttype >= 0) $sql.=", ".MAIN_DB_PREFIX."product as p"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE f.rowid = d.fk_facture"; - if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; - else $sql.=" AND d.fk_product > 0"; - if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; - $sql.= " AND f.fk_soc = s.rowid"; - $sql.= " AND f.entity IN (".getEntity('facture').")"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND f.fk_soc = $socid"; - $sql.=$morefilter; - $sql.= " GROUP BY date_format(f.datef,'%Y%m')"; - $sql.= " ORDER BY date_format(f.datef,'%Y%m') DESC"; - - return $this->_get_stats($sql,$mode, $year); - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return nb of units or supplier invoices in which product is included - * - * @param int $socid Limit count on a particular third party id - * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities - * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only - * @param int $year Year (0=last 12 month) - * @param string $morefilter More sql filters - * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 - */ - function get_nb_achat($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT sum(d.qty), date_format(f.datef, '%Y%m')"; - if ($mode == 'bynumber') $sql.= ", count(DISTINCT f.rowid)"; - $sql.= " FROM ".MAIN_DB_PREFIX."facture_fourn_det as d, ".MAIN_DB_PREFIX."facture_fourn as f, ".MAIN_DB_PREFIX."societe as s"; - if ($filteronproducttype >= 0) $sql.=", ".MAIN_DB_PREFIX."product as p"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE f.rowid = d.fk_facture_fourn"; - if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; - else $sql.=" AND d.fk_product > 0"; - if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; - $sql.= " AND f.fk_soc = s.rowid"; - $sql.= " AND f.entity IN (".getEntity('facture_fourn').")"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND f.fk_soc = $socid"; - $sql.=$morefilter; - $sql.= " GROUP BY date_format(f.datef,'%Y%m')"; - $sql.= " ORDER BY date_format(f.datef,'%Y%m') DESC"; - - return $this->_get_stats($sql,$mode, $year); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return nb of units or proposals in which product is included - * - * @param int $socid Limit count on a particular third party id - * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities - * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only - * @param int $year Year (0=last 12 month) - * @param string $morefilter More sql filters - * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 - */ - function get_nb_propal($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT sum(d.qty), date_format(p.datep, '%Y%m')"; - if ($mode == 'bynumber') $sql.= ", count(DISTINCT p.rowid)"; - $sql.= " FROM ".MAIN_DB_PREFIX."propaldet as d, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."societe as s"; - if ($filteronproducttype >= 0) $sql.=", ".MAIN_DB_PREFIX."product as prod"; - if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE p.rowid = d.fk_propal"; - if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; - else $sql.=" AND d.fk_product > 0"; - if ($filteronproducttype >= 0) $sql.= " AND prod.rowid = d.fk_product AND prod.fk_product_type =".$filteronproducttype; - $sql.= " AND p.fk_soc = s.rowid"; - $sql.= " AND p.entity IN (".getEntity('propal').")"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND p.fk_soc = ".$socid; - $sql.=$morefilter; - $sql.= " GROUP BY date_format(p.datep,'%Y%m')"; - $sql.= " ORDER BY date_format(p.datep,'%Y%m') DESC"; - - return $this->_get_stats($sql,$mode, $year); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return nb of units or proposals in which product is included - * - * @param int $socid Limit count on a particular third party id - * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities - * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only - * @param int $year Year (0=last 12 month) - * @param string $morefilter More sql filters - * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 - */ - function get_nb_propalsupplier($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') - { - // phpcs:enable - global $conf; - global $user; - - $sql = "SELECT sum(d.qty), date_format(p.date_valid, '%Y%m')"; - if ($mode == 'bynumber') $sql.= ", count(DISTINCT p.rowid)"; - $sql.= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."societe as s"; - if ($filteronproducttype >= 0) $sql.=", ".MAIN_DB_PREFIX."product as prod"; - if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE p.rowid = d.fk_supplier_proposal"; - if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; - else $sql.=" AND d.fk_product > 0"; - if ($filteronproducttype >= 0) $sql.= " AND prod.rowid = d.fk_product AND prod.fk_product_type =".$filteronproducttype; - $sql.= " AND p.fk_soc = s.rowid"; - $sql.= " AND p.entity IN (".getEntity('supplier_proposal').")"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND p.fk_soc = ".$socid; - $sql.=$morefilter; - $sql.= " GROUP BY date_format(p.date_valid,'%Y%m')"; - $sql.= " ORDER BY date_format(p.date_valid,'%Y%m') DESC"; - - return $this->_get_stats($sql,$mode, $year); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return nb of units or orders in which product is included - * - * @param int $socid Limit count on a particular third party id - * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities - * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only - * @param int $year Year (0=last 12 month) - * @param string $morefilter More sql filters - * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 - */ - function get_nb_order($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') - { - // phpcs:enable - global $conf, $user; - - $sql = "SELECT sum(d.qty), date_format(c.date_commande, '%Y%m')"; - if ($mode == 'bynumber') $sql.= ", count(DISTINCT c.rowid)"; - $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as d, ".MAIN_DB_PREFIX."commande as c, ".MAIN_DB_PREFIX."societe as s"; - if ($filteronproducttype >= 0) $sql.=", ".MAIN_DB_PREFIX."product as p"; - if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE c.rowid = d.fk_commande"; - if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; - else $sql.=" AND d.fk_product > 0"; - if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; - $sql.= " AND c.fk_soc = s.rowid"; - $sql.= " AND c.entity IN (".getEntity('commande').")"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND c.fk_soc = ".$socid; - $sql.=$morefilter; - $sql.= " GROUP BY date_format(c.date_commande,'%Y%m')"; - $sql.= " ORDER BY date_format(c.date_commande,'%Y%m') DESC"; - - return $this->_get_stats($sql,$mode, $year); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return nb of units or orders in which product is included - * - * @param int $socid Limit count on a particular third party id - * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities - * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only - * @param int $year Year (0=last 12 month) - * @param string $morefilter More sql filters - * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 - */ - function get_nb_ordersupplier($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') - { - // phpcs:enable - global $conf, $user; - - $sql = "SELECT sum(d.qty), date_format(c.date_commande, '%Y%m')"; - if ($mode == 'bynumber') $sql.= ", count(DISTINCT c.rowid)"; - $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as d, ".MAIN_DB_PREFIX."commande_fournisseur as c, ".MAIN_DB_PREFIX."societe as s"; - if ($filteronproducttype >= 0) $sql.=", ".MAIN_DB_PREFIX."product as p"; - if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - $sql.= " WHERE c.rowid = d.fk_commande"; - if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; - else $sql.=" AND d.fk_product > 0"; - if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; - $sql.= " AND c.fk_soc = s.rowid"; - $sql.= " AND c.entity IN (".getEntity('supplier_order').")"; - if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; - if ($socid > 0) $sql.= " AND c.fk_soc = ".$socid; - $sql.=$morefilter; - $sql.= " GROUP BY date_format(c.date_commande,'%Y%m')"; - $sql.= " ORDER BY date_format(c.date_commande,'%Y%m') DESC"; - - return $this->_get_stats($sql,$mode, $year); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Link a product/service to a parent product/service - * - * @param int $id_pere Id of parent product/service - * @param int $id_fils Id of child product/service - * @param int $qty Quantity - * @param int $incdec 1=Increase/decrease stock of child when parent stock increase/decrease - * @return int < 0 if KO, > 0 if OK - */ - function add_sousproduit($id_pere, $id_fils, $qty, $incdec=1) - { - // phpcs:enable - // Clean parameters - if (! is_numeric($id_pere)) $id_pere=0; - if (! is_numeric($id_fils)) $id_fils=0; - if (! is_numeric($incdec)) $incdec=0; - - $result=$this->del_sousproduit($id_pere, $id_fils); - if ($result < 0) return $result; - - // Check not already father of id_pere (to avoid father -> child -> father links) - $sql = 'SELECT fk_product_pere from '.MAIN_DB_PREFIX.'product_association'; - $sql .= ' WHERE fk_product_pere = '.$id_fils.' AND fk_product_fils = '.$id_pere; - if (! $this->db->query($sql)) - { - dol_print_error($this->db); - return -1; - } - else - { - $result = $this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - if($num > 0) - { - $this->error="isFatherOfThis"; - return -1; - } - else - { - $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_association(fk_product_pere,fk_product_fils,qty,incdec)'; - $sql .= ' VALUES ('.$id_pere.', '.$id_fils.', '.$qty.', '.$incdec.')'; - if (! $this->db->query($sql)) - { - dol_print_error($this->db); - return -1; - } - else - { - return 1; - } - } - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Modify composed product - * - * @param int $id_pere Id of parent product/service - * @param int $id_fils Id of child product/service - * @param int $qty Quantity - * @param int $incdec 1=Increase/decrease stock of child when parent stock increase/decrease - * @return int < 0 if KO, > 0 if OK - */ - function update_sousproduit($id_pere, $id_fils, $qty, $incdec=1) - { - // phpcs:enable - // Clean parameters - if (! is_numeric($id_pere)) $id_pere=0; - if (! is_numeric($id_fils)) $id_fils=0; - if (! is_numeric($incdec)) $incdec=1; - if (! is_numeric($qty)) $qty=1; - - $sql = 'UPDATE '.MAIN_DB_PREFIX.'product_association SET '; - $sql.= 'qty='.$qty; - $sql.= ',incdec='.$incdec; - $sql .= ' WHERE fk_product_pere='.$id_pere.' AND fk_product_fils='.$id_fils; - - if (!$this->db->query($sql)) - { - dol_print_error($this->db); - return -1; - } - else - { - return 1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Retire le lien entre un sousproduit et un produit/service - * - * @param int $fk_parent Id du produit auquel ne sera plus lie le produit lie - * @param int $fk_child Id du produit a ne plus lie - * @return int < 0 if KO, > 0 if OK - */ - function del_sousproduit($fk_parent, $fk_child) - { - // phpcs:enable - if (! is_numeric($fk_parent)) $fk_parent=0; - if (! is_numeric($fk_child)) $fk_child=0; - - $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_association"; - $sql.= " WHERE fk_product_pere = ".$fk_parent; - $sql.= " AND fk_product_fils = ".$fk_child; - - dol_syslog(get_class($this).'::del_sousproduit', LOG_DEBUG); - if (! $this->db->query($sql)) - { - dol_print_error($this->db); - return -1; - } - - return 1; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Verifie si c'est un sous-produit - * - * @param int $fk_parent Id du produit auquel le produit est lie - * @param int $fk_child Id du produit lie - * @return int < 0 si erreur, > 0 si ok - */ - function is_sousproduit($fk_parent, $fk_child) - { - // phpcs:enable - $sql = "SELECT fk_product_pere, qty, incdec"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_association"; - $sql.= " WHERE fk_product_pere = '".$fk_parent."'"; - $sql.= " AND fk_product_fils = '".$fk_child."'"; - - $result = $this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - - if($num > 0) - { - $obj = $this->db->fetch_object($result); - $this->is_sousproduit_qty = $obj->qty; - $this->is_sousproduit_incdec = $obj->incdec; - - return true; - } - else - { - return false; - } - } - else - { - dol_print_error($this->db); - return -1; - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Add a supplier price for the product. - * Note: Duplicate ref is accepted for different quantity only, or for different companies. - * - * @param User $user User that make link - * @param int $id_fourn Supplier id - * @param string $ref_fourn Supplier ref - * @param float $quantity Quantity minimum for price - * @return int < 0 if KO, 0 if link already exists for this product, > 0 if OK - */ - function add_fournisseur($user, $id_fourn, $ref_fourn, $quantity) - { - // phpcs:enable - global $conf; - - $now=dol_now(); - - dol_syslog(get_class($this)."::add_fournisseur id_fourn = ".$id_fourn." ref_fourn=".$ref_fourn." quantity=".$quantity, LOG_DEBUG); - - if ($ref_fourn) - { - $sql = "SELECT rowid, fk_product"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; - $sql.= " WHERE fk_soc = ".$id_fourn; - $sql.= " AND ref_fourn = '".$this->db->escape($ref_fourn)."'"; - $sql.= " AND fk_product != ".$this->id; - $sql.= " AND entity IN (".getEntity('productsupplierprice').")"; - - $resql=$this->db->query($sql); - if ($resql) - { - $obj = $this->db->fetch_object($resql); - if ($obj) - { - // If the supplier ref already exists but for another product (duplicate ref is accepted for different quantity only or different companies) - $this->product_id_already_linked = $obj->fk_product; - return -3; - } - $this->db->free($resql); - } - } - - $sql = "SELECT rowid"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; - $sql.= " WHERE fk_soc = ".$id_fourn; - if ($ref_fourn) $sql.= " AND ref_fourn = '".$this->db->escape($ref_fourn)."'"; - else $sql.= " AND (ref_fourn = '' OR ref_fourn IS NULL)"; - $sql.= " AND quantity = '".$quantity."'"; - $sql.= " AND fk_product = ".$this->id; - $sql.= " AND entity IN (".getEntity('productsupplierprice').")"; - - $resql=$this->db->query($sql); - if ($resql) - { - $obj = $this->db->fetch_object($resql); - - // The reference supplier does not exist, we create it for this product. - if (! $obj) - { - $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_fournisseur_price("; - $sql.= "datec"; - $sql.= ", entity"; - $sql.= ", fk_product"; - $sql.= ", fk_soc"; - $sql.= ", ref_fourn"; - $sql.= ", quantity"; - $sql.= ", fk_user"; - $sql.= ", tva_tx"; - $sql.= ") VALUES ("; - $sql.= "'".$this->db->idate($now)."'"; - $sql.= ", ".$conf->entity; - $sql.= ", ".$this->id; - $sql.= ", ".$id_fourn; - $sql.= ", '".$this->db->escape($ref_fourn)."'"; - $sql.= ", ".$quantity; - $sql.= ", ".$user->id; - $sql.= ", 0"; - $sql.= ")"; - - if ($this->db->query($sql)) - { - $this->product_fourn_price_id = $this->db->last_insert_id(MAIN_DB_PREFIX."product_fournisseur_price"); - return 1; - } - else - { - $this->error=$this->db->lasterror(); - return -1; - } - } - // If the supplier price already exists for this product and quantity - else - { - $this->product_fourn_price_id = $obj->rowid; - return 0; - } - } - else - { - $this->error=$this->db->lasterror(); - return -2; - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Renvoie la liste des fournisseurs du produit/service - * - * @return array Tableau des id de fournisseur - */ - function list_suppliers() - { - // phpcs:enable - global $conf; - - $list = array(); - - $sql = "SELECT DISTINCT p.fk_soc"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as p"; - $sql.= " WHERE p.fk_product = ".$this->id; - $sql.= " AND p.entity = ".$conf->entity; - - $result = $this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - $i=0; - while ($i < $num) - { - $obj = $this->db->fetch_object($result); - $list[$i] = $obj->fk_soc; - $i++; - } - } - - return $list; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Recopie les prix d'un produit/service sur un autre - * - * @param int $fromId Id product source - * @param int $toId Id product target - * @return int < 0 if KO, > 0 if OK - */ - function clone_price($fromId, $toId) - { - // phpcs:enable - $this->db->begin(); - - // les prix - $sql = "INSERT ".MAIN_DB_PREFIX."product_price ("; - $sql.= " fk_product, date_price, price, tva_tx, localtax1_tx, localtax2_tx, fk_user_author, tosell)"; - $sql.= " SELECT ".$toId . ", date_price, price, tva_tx, localtax1_tx, localtax2_tx, fk_user_author, tosell"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_price "; - $sql.= " WHERE fk_product = ". $fromId; - - dol_syslog(get_class($this).'::clone_price', LOG_DEBUG); - if (! $this->db->query($sql)) - { - $this->db->rollback(); - return -1; - } - $this->db->commit(); - return 1; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Clone links between products - * - * @param int $fromId Product id - * @param int $toId Product id - * @return int <0 if KO, >0 if OK - */ - function clone_associations($fromId, $toId) - { - // phpcs:enable - $this->db->begin(); - - $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_association (fk_product_pere, fk_product_fils, qty)'; - $sql.= " SELECT ".$toId.", fk_product_fils, qty FROM ".MAIN_DB_PREFIX."product_association"; - $sql.= " WHERE fk_product_pere = ".$fromId; - - dol_syslog(get_class($this).'::clone_association', LOG_DEBUG); - if (! $this->db->query($sql)) - { - $this->db->rollback(); - return -1; - } - - $this->db->commit(); - return 1; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Recopie les fournisseurs et prix fournisseurs d'un produit/service sur un autre - * - * @param int $fromId Id produit source - * @param int $toId Id produit cible - * @return int < 0 si erreur, > 0 si ok - */ - function clone_fournisseurs($fromId, $toId) - { - // phpcs:enable - $this->db->begin(); - - $now=dol_now(); - - // les fournisseurs - /*$sql = "INSERT ".MAIN_DB_PREFIX."product_fournisseur (" - . " datec, fk_product, fk_soc, ref_fourn, fk_user_author )" - . " SELECT '".$this->db->idate($now)."', ".$toId.", fk_soc, ref_fourn, fk_user_author" - . " FROM ".MAIN_DB_PREFIX."product_fournisseur" - . " WHERE fk_product = ".$fromId; - - if ( ! $this->db->query($sql ) ) - { - $this->db->rollback(); - return -1; - }*/ - - // les prix de fournisseurs. - $sql = "INSERT ".MAIN_DB_PREFIX."product_fournisseur_price ("; - $sql.= " datec, fk_product, fk_soc, price, quantity, fk_user)"; - $sql.= " SELECT '".$this->db->idate($now)."', ".$toId. ", fk_soc, price, quantity, fk_user"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; - $sql.= " WHERE fk_product = ".$fromId; - - dol_syslog(get_class($this).'::clone_fournisseurs', LOG_DEBUG); - $resql=$this->db->query($sql); - if (! $resql) - { - $this->db->rollback(); - return -1; - } - else - { - $this->db->commit(); - return 1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Fonction recursive uniquement utilisee par get_arbo_each_prod, recompose l'arborescence des sousproduits - * Define value of this->res - * - * @param array $prod Products array - * @param string $compl_path Directory path of parents to add before - * @param int $multiply Because each sublevel must be multiplicated by parent nb - * @param int $level Init level - * @param int $id_parent Id parent - * @return void - */ - function fetch_prod_arbo($prod, $compl_path="", $multiply=1, $level=1, $id_parent=0) - { - // phpcs:enable - global $conf,$langs; - - $product = new Product($this->db); - //var_dump($prod); - foreach($prod as $id_product => $desc_pere) // $id_product is 0 (first call starting with root top) or an id of a sub_product - { - if (is_array($desc_pere)) // If desc_pere is an array, this means it's a child - { - $id=(! empty($desc_pere[0]) ? $desc_pere[0] :''); - $nb=(! empty($desc_pere[1]) ? $desc_pere[1] :''); - $type=(! empty($desc_pere[2]) ? $desc_pere[2] :''); - $label=(! empty($desc_pere[3]) ? $desc_pere[3] :''); - $incdec=!empty($desc_pere[4]) ? $desc_pere[4] : 0; - - if ($multiply < 1) $multiply=1; - - //print "XXX We add id=".$id." - label=".$label." - nb=".$nb." - multiply=".$multiply." fullpath=".$compl_path.$label."\n"; - $this->fetch($id); // Load product - $this->load_stock('nobatch,novirtual'); // Load stock to get true this->stock_reel - $this->res[]= array( - 'id'=>$id, // Id product - 'id_parent'=>$id_parent, - 'ref'=>$this->ref, // Ref product - 'nb'=>$nb, // Nb of units that compose parent product - 'nb_total'=>$nb*$multiply, // Nb of units for all nb of product - 'stock'=>$this->stock_reel, // Stock - 'stock_alert'=>$this->seuil_stock_alerte, // Stock alert - 'label'=>$label, - 'fullpath'=>$compl_path.$label, // Label - 'type'=>$type, // Nb of units that compose parent product - 'desiredstock'=>$this->desiredstock, - 'level'=>$level, - 'incdec'=>$incdec, - 'entity'=>$this->entity - ); - - // Recursive call if there is childs to child - if (is_array($desc_pere['childs'])) - { - //print 'YYY We go down for '.$desc_pere[3]." -> \n"; - $this->fetch_prod_arbo($desc_pere['childs'], $compl_path.$desc_pere[3]." -> ", $desc_pere[1]*$multiply, $level+1, $id); - } - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Build the tree of subproducts into an array - * this->sousprods is loaded by this->get_sousproduits_arbo() - * - * @param int $multiply Because each sublevel must be multiplicated by parent nb - * @return array $this->res - */ - function get_arbo_each_prod($multiply=1) - { - // phpcs:enable - $this->res = array(); - if (isset($this->sousprods) && is_array($this->sousprods)) - { - foreach($this->sousprods as $prod_name => $desc_product) - { - if (is_array($desc_product)) $this->fetch_prod_arbo($desc_product,"",$multiply,1,$this->id); - } - } - //var_dump($this->res); - return $this->res; - } - - /** - * Return all parent products for current product (first level only) - * - * @return int Nb of father + child - */ - public function hasFatherOrChild() - { - $nb = 0; - - $sql = "SELECT COUNT(pa.rowid) as nb"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_association as pa"; - $sql.= " WHERE pa.fk_product_fils = ".$this->id." OR pa.fk_product_pere = ".$this->id; - $resql = $this->db->query($sql); - if ($resql) - { - $obj = $this->db->fetch_object($resql); - if ($obj) $nb = $obj->nb; - } - else - { - return -1; - } - - return $nb; - } - - /** - * Return if a product has variants or not - * - * @return int Number of variants - */ - public function hasVariants() - { - $nb = 0; - $sql = "SELECT count(rowid) as nb FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_parent = ".$this->id; - $sql.= " AND entity IN (".getEntity('product').")"; - - $resql = $this->db->query($sql); - if ($resql) { - $obj = $this->db->fetch_object($resql); - if ($obj) $nb = $obj->nb; - } - - return $nb; - } - - - /** - * Return if loaded product is a variant - * - * @return int - */ - public function isVariant() - { - global $conf; - if (!empty($conf->variants->enabled)) { - $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "product_attribute_combination WHERE fk_product_child = " . $this->id . " AND entity IN (" . getEntity('product') . ")"; - - $query = $this->db->query($sql); - - if ($query) { - if (!$this->db->num_rows($query)) { - return false; - } - return true; - } else { - dol_print_error($this->db); - return -1; - } - } else { - return false; - } - } - - /** - * Return all parent products for current product (first level only) - * - * @return array Array of product - */ - public function getFather() - { - $sql = "SELECT p.rowid, p.label as label, p.ref as ref, pa.fk_product_pere as id, p.fk_product_type, pa.qty, pa.incdec, p.entity"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_association as pa,"; - $sql.= " ".MAIN_DB_PREFIX."product as p"; - $sql.= " WHERE p.rowid = pa.fk_product_pere"; - $sql.= " AND pa.fk_product_fils = ".$this->id; - - $res = $this->db->query($sql); - if ($res) - { - $prods = array (); - while ($record = $this->db->fetch_array($res)) - { - // $record['id'] = $record['rowid'] = id of father - $prods[$record['id']]['id'] = $record['rowid']; - $prods[$record['id']]['ref'] = $record['ref']; - $prods[$record['id']]['label'] = $record['label']; - $prods[$record['id']]['qty'] = $record['qty']; - $prods[$record['id']]['incdec'] = $record['incdec']; - $prods[$record['id']]['fk_product_type'] = $record['fk_product_type']; - $prods[$record['id']]['entity'] = $record['entity']; - } - return $prods; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - - /** - * Return childs of product $id - * - * @param int $id Id of product to search childs of - * @param int $firstlevelonly Return only direct child - * @param int $level Level of recursing call (start to 1) - * @return array Return array(prodid=>array(0=prodid, 1=>qty, 2=> ...) - */ - public function getChildsArbo($id, $firstlevelonly=0, $level=1) - { - global $alreadyfound; - - $sql = "SELECT p.rowid, p.label as label, pa.qty as qty, pa.fk_product_fils as id, p.fk_product_type, pa.incdec"; - $sql.= " FROM ".MAIN_DB_PREFIX."product as p"; - $sql.= ", ".MAIN_DB_PREFIX."product_association as pa"; - $sql.= " WHERE p.rowid = pa.fk_product_fils"; - $sql.= " AND pa.fk_product_pere = ".$id; - $sql.= " AND pa.fk_product_fils != ".$id; // This should not happens, it is to avoid infinite loop if it happens - - dol_syslog(get_class($this).'::getChildsArbo id='.$id.' level='.$level, LOG_DEBUG); - - if ($level == 1) $alreadyfound=array($id=>1); // We init array of found object to start of tree, so if we found it later (should not happened), we stop immediatly - // Protection against infinite loop - if ($level > 30) return array(); - - $res = $this->db->query($sql); - if ($res) - { - $prods = array(); - while ($rec = $this->db->fetch_array($res)) - { - if (! empty($alreadyfound[$rec['rowid']])) - { - dol_syslog(get_class($this).'::getChildsArbo the product id='.$rec['rowid'].' was already found at a higher level in tree. We discard to avoid infinite loop', LOG_WARNING); - continue; - } - $alreadyfound[$rec['rowid']]=1; - $prods[$rec['rowid']]= array( - 0=>$rec['rowid'], - 1=>$rec['qty'], - 2=>$rec['fk_product_type'], - 3=>$this->db->escape($rec['label']), - 4=>$rec['incdec'] - ); - //$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty'],2=>$rec['fk_product_type']); - //$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty']); - if (empty($firstlevelonly)) - { - $listofchilds=$this->getChildsArbo($rec['rowid'], 0, $level + 1); - foreach($listofchilds as $keyChild => $valueChild) - { - $prods[$rec['rowid']]['childs'][$keyChild] = $valueChild; - } - } - } - - return $prods; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return tree of all subproducts for product. Tree contains id, name and quantity. - * Set this->sousprods - * - * @return void - */ - function get_sousproduits_arbo() - { - // phpcs:enable - $parent=array(); - - foreach($this->getChildsArbo($this->id) as $keyChild => $valueChild) // Warning. getChildsArbo can call getChildsArbo recursively. Starting point is $value[0]=id of product - { - $parent[$this->label][$keyChild] = $valueChild; - } - foreach($parent as $key => $value) // key=label, value is array of childs - { - $this->sousprods[$key] = $value; - } - } - - /** - * Return clicable link of object (with eventually picto) - * - * @param int $withpicto Add picto into link - * @param string $option Where point the link ('stock', 'composition', 'category', 'supplier', '') - * @param int $maxlength Maxlength of ref - * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking - * @return string String with URL - */ - public function getNomUrl($withpicto=0, $option='', $maxlength=0, $save_lastsearch_value=-1) - { - global $conf, $langs, $hookmanager; - include_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; - - $result=''; - $newref=$this->ref; - if ($maxlength) $newref=dol_trunc($newref,$maxlength,'middle'); - - if ($this->type == Product::TYPE_PRODUCT) $label = '' . $langs->trans("ShowProduct") . ''; - if ($this->type == Product::TYPE_SERVICE) $label = '' . $langs->trans("ShowService") . ''; - if (! empty($this->ref)) - $label .= '
' . $langs->trans('ProductRef') . ': ' . $this->ref; - if (! empty($this->label)) - $label .= '
' . $langs->trans('ProductLabel') . ': ' . $this->label; - - if ($this->type == Product::TYPE_PRODUCT) + if (!empty($conf->dynamicprices->enabled) && !empty($this->fk_price_expression) && empty($ignore_expression)) { + include_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; + $priceparser = new PriceParser($this->db); + $price_result = $priceparser->parseProduct($this); + if ($price_result >= 0) { + $this->price = $price_result; + // Calculate the VAT + $this->price_ttc = price2num($this->price) * (1 + ($this->tva_tx / 100)); + $this->price_ttc = price2num($this->price_ttc, 'MU'); + } + } + + // We should not load stock during the fetch. If someone need stock of product, he must call load_stock after fetching product. + // Instead we just init the stock_warehouse array + $this->stock_warehouse = array(); + + return 1; + } + else + { + return 0; + } + } + else { - if ($this->weight) $label.="
".$langs->trans("Weight").': '.$this->weight.' '.measuring_units_string($this->weight_units,"weight"); - if ($this->length) $label.="
".$langs->trans("Length").': '.$this->length.' '.measuring_units_string($this->length_units,'length'); - if ($this->surface) $label.="
".$langs->trans("Surface").': '.$this->surface.' '.measuring_units_string($this->surface_units,'surface'); - if ($this->volume) $label.="
".$langs->trans("Volume").': '.$this->volume.' '.measuring_units_string($this->volume_units,'volume'); + dol_print_error($this->db); + return -1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats propale pour le produit/service + * + * @param int $socid Id societe + * @return array Tableau des stats + */ + function load_stats_propale($socid=0) + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT COUNT(DISTINCT p.fk_soc) as nb_customers, COUNT(DISTINCT p.rowid) as nb,"; + $sql.= " COUNT(pd.rowid) as nb_rows, SUM(pd.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."propaldet as pd"; + $sql.= ", ".MAIN_DB_PREFIX."propal as p"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid) { $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE p.rowid = pd.fk_propal"; + $sql.= " AND p.fk_soc = s.rowid"; + $sql.= " AND p.entity IN (".getEntity('propal').")"; + $sql.= " AND pd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid) { $sql .= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + //$sql.= " AND pr.fk_statut != 0"; + if ($socid > 0) { $sql.= " AND p.fk_soc = ".$socid; } - if ($this->type == Product::TYPE_PRODUCT || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_propale['customers']=$obj->nb_customers; + $this->stats_propale['nb']=$obj->nb; + $this->stats_propale['rows']=$obj->nb_rows; + $this->stats_propale['qty']=$obj->qty?$obj->qty:0; + return 1; + } + else { - if (! empty($conf->productbatch->enabled)) + $this->error=$this->db->error(); + return -1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats propale pour le produit/service + * + * @param int $socid Id thirdparty + * @return array Tableau des stats + */ + function load_stats_proposal_supplier($socid=0) + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT COUNT(DISTINCT p.fk_soc) as nb_suppliers, COUNT(DISTINCT p.rowid) as nb,"; + $sql.= " COUNT(pd.rowid) as nb_rows, SUM(pd.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as pd"; + $sql.= ", ".MAIN_DB_PREFIX."supplier_proposal as p"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid) { $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE p.rowid = pd.fk_supplier_proposal"; + $sql.= " AND p.fk_soc = s.rowid"; + $sql.= " AND p.entity IN (".getEntity('supplier_proposal').")"; + $sql.= " AND pd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid) { $sql .= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + //$sql.= " AND pr.fk_statut != 0"; + if ($socid > 0) { $sql.= " AND p.fk_soc = ".$socid; + } + + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_proposal_supplier['suppliers']=$obj->nb_suppliers; + $this->stats_proposal_supplier['nb']=$obj->nb; + $this->stats_proposal_supplier['rows']=$obj->nb_rows; + $this->stats_proposal_supplier['qty']=$obj->qty?$obj->qty:0; + return 1; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats commande client pour le produit/service + * + * @param int $socid Id societe pour filtrer sur une societe + * @param string $filtrestatut Id statut pour filtrer sur un statut + * @param int $forVirtualStock Ignore rights filter for virtual stock calculation. + * @return array Array of stats (nb=nb of order, qty=qty ordered) + */ + function load_stats_commande($socid=0,$filtrestatut='', $forVirtualStock = 0) + { + // phpcs:enable + global $conf,$user; + + $sql = "SELECT COUNT(DISTINCT c.fk_soc) as nb_customers, COUNT(DISTINCT c.rowid) as nb,"; + $sql.= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd"; + $sql.= ", ".MAIN_DB_PREFIX."commande as c"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE c.rowid = cd.fk_commande"; + $sql.= " AND c.fk_soc = s.rowid"; + $sql.= " AND c.entity IN (".getEntity('commande').")"; + $sql.= " AND cd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) { $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND c.fk_soc = ".$socid; + } + if ($filtrestatut <> '') { $sql.= " AND c.fk_statut in (".$filtrestatut.")"; + } + + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_commande['customers']=$obj->nb_customers; + $this->stats_commande['nb']=$obj->nb; + $this->stats_commande['rows']=$obj->nb_rows; + $this->stats_commande['qty']=$obj->qty?$obj->qty:0; + + // if it's a virtual product, maybe it is in order by extension + if (! empty($conf->global->ORDER_ADD_ORDERS_WITH_PARENT_PROD_IF_INCDEC)) { + $TFather = $this->getFather(); + if (is_array($TFather) && !empty($TFather)) { + foreach($TFather as &$fatherData) { + $pFather = new Product($this->db); + $pFather->id = $fatherData['id']; + $qtyCoef = $fatherData['qty']; + + if ($fatherData['incdec']) { + $pFather->load_stats_commande($socid, $filtrestatut); + + $this->stats_commande['customers']+=$pFather->stats_commande['customers']; + $this->stats_commande['nb']+=$pFather->stats_commande['nb']; + $this->stats_commande['rows']+=$pFather->stats_commande['rows']; + $this->stats_commande['qty']+=$pFather->stats_commande['qty'] * $qtyCoef; + } + } + } + } + + // If stock decrease is on invoice validation, the theorical stock continue to + // count the orders to ship in theorical stock when some are already removed b invoice validation. + // If option DECREASE_ONLY_UNINVOICEDPRODUCTS is on, we make a compensation. + if (! empty($conf->global->STOCK_CALCULATE_ON_BILL)) { + if (! empty($conf->global->DECREASE_ONLY_UNINVOICEDPRODUCTS)) { + $adeduire = 0; + $sql = "SELECT sum(fd.qty) as count FROM ".MAIN_DB_PREFIX."facturedet fd "; + $sql .= " JOIN ".MAIN_DB_PREFIX."facture f ON fd.fk_facture = f.rowid "; + $sql .= " JOIN ".MAIN_DB_PREFIX."element_element el ON el.fk_target = f.rowid and el.targettype = 'facture' and sourcetype = 'commande'"; + $sql .= " JOIN ".MAIN_DB_PREFIX."commande c ON el.fk_source = c.rowid "; + $sql .= " WHERE c.fk_statut IN (".$filtrestatut.") AND c.facture = 0 AND fd.fk_product = ".$this->id; + dol_syslog(__METHOD__.":: sql $sql", LOG_NOTICE); + + $resql = $this->db->query($sql); + if ($resql ) { + if ($this->db->num_rows($resql) > 0) { + $obj = $this->db->fetch_object($resql); + $adeduire += $obj->count; + } + } + + $this->stats_commande['qty'] -= $adeduire; + } + } + + return 1; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats commande fournisseur pour le produit/service + * + * @param int $socid Id societe pour filtrer sur une societe + * @param string $filtrestatut Id des statuts pour filtrer sur des statuts + * @param int $forVirtualStock Ignore rights filter for virtual stock calculation. + * @return array Tableau des stats + */ + function load_stats_commande_fournisseur($socid=0,$filtrestatut='', $forVirtualStock = 0) + { + // phpcs:enable + global $conf,$user; + + $sql = "SELECT COUNT(DISTINCT c.fk_soc) as nb_suppliers, COUNT(DISTINCT c.rowid) as nb,"; + $sql.= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd"; + $sql.= ", ".MAIN_DB_PREFIX."commande_fournisseur as c"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE c.rowid = cd.fk_commande"; + $sql.= " AND c.fk_soc = s.rowid"; + $sql.= " AND c.entity IN (".getEntity('supplier_order').")"; + $sql.= " AND cd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) { $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND c.fk_soc = ".$socid; + } + if ($filtrestatut != '') { $sql.= " AND c.fk_statut in (".$filtrestatut.")"; // Peut valoir 0 + } + + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_commande_fournisseur['suppliers']=$obj->nb_suppliers; + $this->stats_commande_fournisseur['nb']=$obj->nb; + $this->stats_commande_fournisseur['rows']=$obj->nb_rows; + $this->stats_commande_fournisseur['qty']=$obj->qty?$obj->qty:0; + return 1; + } + else + { + $this->error=$this->db->error().' sql='.$sql; + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats expedition client pour le produit/service + * + * @param int $socid Id societe pour filtrer sur une societe + * @param string $filtrestatut Id statut pour filtrer sur un statut + * @param int $forVirtualStock Ignore rights filter for virtual stock calculation. + * @return array Tableau des stats + */ + function load_stats_sending($socid=0,$filtrestatut='', $forVirtualStock = 0) + { + // phpcs:enable + global $conf,$user; + + $sql = "SELECT COUNT(DISTINCT e.fk_soc) as nb_customers, COUNT(DISTINCT e.rowid) as nb,"; + $sql.= " COUNT(ed.rowid) as nb_rows, SUM(ed.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed"; + $sql.= ", ".MAIN_DB_PREFIX."commandedet as cd"; + $sql.= ", ".MAIN_DB_PREFIX."commande as c"; + $sql.= ", ".MAIN_DB_PREFIX."expedition as e"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE e.rowid = ed.fk_expedition"; + $sql.= " AND c.rowid = cd.fk_commande"; + $sql.= " AND e.fk_soc = s.rowid"; + $sql.= " AND e.entity IN (".getEntity('expedition').")"; + $sql.= " AND ed.fk_origin_line = cd.rowid"; + $sql.= " AND cd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) { $sql.= " AND e.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND e.fk_soc = ".$socid; + } + if ($filtrestatut <> '') { $sql.= " AND c.fk_statut in (".$filtrestatut.")"; + } + + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_expedition['customers']=$obj->nb_customers; + $this->stats_expedition['nb']=$obj->nb; + $this->stats_expedition['rows']=$obj->nb_rows; + $this->stats_expedition['qty']=$obj->qty?$obj->qty:0; + return 1; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats réception fournisseur pour le produit/service + * + * @param int $socid Id societe pour filtrer sur une societe + * @param string $filtrestatut Id statut pour filtrer sur un statut + * @param int $forVirtualStock Ignore rights filter for virtual stock calculation. + * @return array Tableau des stats + */ + function load_stats_reception($socid=0,$filtrestatut='', $forVirtualStock = 0) + { + // phpcs:enable + global $conf,$user; + + $sql = "SELECT COUNT(DISTINCT cf.fk_soc) as nb_customers, COUNT(DISTINCT cf.rowid) as nb,"; + $sql.= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as fd"; + $sql.= ", ".MAIN_DB_PREFIX."commande_fournisseur as cf"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE cf.rowid = fd.fk_commande"; + $sql.= " AND cf.fk_soc = s.rowid"; + $sql.= " AND cf.entity IN (".getEntity('supplier_order').")"; + $sql.= " AND fd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid && !$forVirtualStock) { $sql.= " AND cf.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND cf.fk_soc = ".$socid; + } + if ($filtrestatut <> '') { $sql.= " AND cf.fk_statut in (".$filtrestatut.")"; + } + + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_reception['suppliers']=$obj->nb_customers; + $this->stats_reception['nb']=$obj->nb; + $this->stats_reception['rows']=$obj->nb_rows; + $this->stats_reception['qty']=$obj->qty?$obj->qty:0; + return 1; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats contrat pour le produit/service + * + * @param int $socid Id societe + * @return array Tableau des stats + */ + function load_stats_contrat($socid=0) + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT COUNT(DISTINCT c.fk_soc) as nb_customers, COUNT(DISTINCT c.rowid) as nb,"; + $sql.= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."contratdet as cd"; + $sql.= ", ".MAIN_DB_PREFIX."contrat as c"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE c.rowid = cd.fk_contrat"; + $sql.= " AND c.fk_soc = s.rowid"; + $sql.= " AND c.entity IN (".getEntity('contract').")"; + $sql.= " AND cd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + //$sql.= " AND c.statut != 0"; + if ($socid > 0) { $sql.= " AND c.fk_soc = ".$socid; + } + + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_contrat['customers']=$obj->nb_customers; + $this->stats_contrat['nb']=$obj->nb; + $this->stats_contrat['rows']=$obj->nb_rows; + $this->stats_contrat['qty']=$obj->qty?$obj->qty:0; + return 1; + } + else + { + $this->error=$this->db->error().' sql='.$sql; + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats facture pour le produit/service + * + * @param int $socid Id societe + * @return array Tableau des stats + */ + function load_stats_facture($socid=0) + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT COUNT(DISTINCT f.fk_soc) as nb_customers, COUNT(DISTINCT f.rowid) as nb,"; + $sql.= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."facturedet as fd"; + $sql.= ", ".MAIN_DB_PREFIX."facture as f"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE f.rowid = fd.fk_facture"; + $sql.= " AND f.fk_soc = s.rowid"; + $sql.= " AND f.entity IN (".getEntity('facture').")"; + $sql.= " AND fd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + //$sql.= " AND f.fk_statut != 0"; + if ($socid > 0) { $sql .= " AND f.fk_soc = ".$socid; + } + + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_facture['customers']=$obj->nb_customers; + $this->stats_facture['nb']=$obj->nb; + $this->stats_facture['rows']=$obj->nb_rows; + $this->stats_facture['qty']=$obj->qty?$obj->qty:0; + return 1; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Charge tableau des stats facture pour le produit/service + * + * @param int $socid Id societe + * @return array Tableau des stats + */ + function load_stats_facture_fournisseur($socid=0) + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT COUNT(DISTINCT f.fk_soc) as nb_suppliers, COUNT(DISTINCT f.rowid) as nb,"; + $sql.= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty"; + $sql.= " FROM ".MAIN_DB_PREFIX."facture_fourn_det as fd"; + $sql.= ", ".MAIN_DB_PREFIX."facture_fourn as f"; + $sql.= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE f.rowid = fd.fk_facture_fourn"; + $sql.= " AND f.fk_soc = s.rowid"; + $sql.= " AND f.entity IN (".getEntity('facture_fourn').")"; + $sql.= " AND fd.fk_product = ".$this->id; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + //$sql.= " AND f.fk_statut != 0"; + if ($socid > 0) { $sql .= " AND f.fk_soc = ".$socid; + } + + $result = $this->db->query($sql); + if ($result ) { + $obj=$this->db->fetch_object($result); + $this->stats_facture_fournisseur['suppliers']=$obj->nb_suppliers; + $this->stats_facture_fournisseur['nb']=$obj->nb; + $this->stats_facture_fournisseur['rows']=$obj->nb_rows; + $this->stats_facture_fournisseur['qty']=$obj->qty?$obj->qty:0; + return 1; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return an array formated for showing graphs + * + * @param string $sql Request to execute + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $year Year (0=current year) + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function _get_stats($sql, $mode, $year=0) + { + // phpcs:enable + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { - $langs->load("productbatch"); - $label.="
".$langs->trans("ManageLotSerial").': '.$this->getLibStatut(0,2); + $arr = $this->db->fetch_array($resql); + if ($mode == 'byunit') { $tab[$arr[1]] = $arr[0]; // 1st field + } + if ($mode == 'bynumber') { $tab[$arr[1]] = $arr[2]; // 3rd field + } + $i++; + } + } + else + { + $this->error=$this->db->error().' sql='.$sql; + return -1; + } + + if (empty($year)) { + $year = strftime('%Y', time()); + $month = strftime('%m', time()); + } + else + { + $month=12; // We imagine we are at end of year, so we get last 12 month before, so all correct year. + } + $result = array(); + + for ($j = 0 ; $j < 12 ; $j++) + { + $idx=ucfirst(dol_trunc(dol_print_date(dol_mktime(12, 0, 0, $month, 1, $year), "%b"), 3, 'right', 'UTF-8', 1)); + $monthnum=sprintf("%02s", $month); + + $result[$j] = array($idx,isset($tab[$year.$month])?$tab[$year.$month]:0); + // $result[$j] = array($monthnum,isset($tab[$year.$month])?$tab[$year.$month]:0); + + $month = "0".($month - 1); + if (dol_strlen($month) == 3) { + $month = substr($month, 1); + } + if ($month == 0) { + $month = 12; + $year = $year - 1; + } + } + + return array_reverse($result); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return nb of units or customers invoices in which product is included + * + * @param int $socid Limit count on a particular third party id + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only + * @param int $year Year (0=last 12 month) + * @param string $morefilter More sql filters + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function get_nb_vente($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT sum(d.qty), date_format(f.datef, '%Y%m')"; + if ($mode == 'bynumber') { $sql.= ", count(DISTINCT f.rowid)"; + } + $sql.= " FROM ".MAIN_DB_PREFIX."facturedet as d, ".MAIN_DB_PREFIX."facture as f, ".MAIN_DB_PREFIX."societe as s"; + if ($filteronproducttype >= 0) { $sql.=", ".MAIN_DB_PREFIX."product as p"; + } + if (!$user->rights->societe->client->voir && !$socid) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE f.rowid = d.fk_facture"; + if ($this->id > 0) { $sql.= " AND d.fk_product =".$this->id; + } else { $sql.=" AND d.fk_product > 0"; + } + if ($filteronproducttype >= 0) { $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; + } + $sql.= " AND f.fk_soc = s.rowid"; + $sql.= " AND f.entity IN (".getEntity('facture').")"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND f.fk_soc = $socid"; + } + $sql.=$morefilter; + $sql.= " GROUP BY date_format(f.datef,'%Y%m')"; + $sql.= " ORDER BY date_format(f.datef,'%Y%m') DESC"; + + return $this->_get_stats($sql, $mode, $year); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return nb of units or supplier invoices in which product is included + * + * @param int $socid Limit count on a particular third party id + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only + * @param int $year Year (0=last 12 month) + * @param string $morefilter More sql filters + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function get_nb_achat($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT sum(d.qty), date_format(f.datef, '%Y%m')"; + if ($mode == 'bynumber') { $sql.= ", count(DISTINCT f.rowid)"; + } + $sql.= " FROM ".MAIN_DB_PREFIX."facture_fourn_det as d, ".MAIN_DB_PREFIX."facture_fourn as f, ".MAIN_DB_PREFIX."societe as s"; + if ($filteronproducttype >= 0) { $sql.=", ".MAIN_DB_PREFIX."product as p"; + } + if (!$user->rights->societe->client->voir && !$socid) { $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE f.rowid = d.fk_facture_fourn"; + if ($this->id > 0) { $sql.= " AND d.fk_product =".$this->id; + } else { $sql.=" AND d.fk_product > 0"; + } + if ($filteronproducttype >= 0) { $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; + } + $sql.= " AND f.fk_soc = s.rowid"; + $sql.= " AND f.entity IN (".getEntity('facture_fourn').")"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND f.fk_soc = $socid"; + } + $sql.=$morefilter; + $sql.= " GROUP BY date_format(f.datef,'%Y%m')"; + $sql.= " ORDER BY date_format(f.datef,'%Y%m') DESC"; + + return $this->_get_stats($sql, $mode, $year); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return nb of units or proposals in which product is included + * + * @param int $socid Limit count on a particular third party id + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only + * @param int $year Year (0=last 12 month) + * @param string $morefilter More sql filters + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function get_nb_propal($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT sum(d.qty), date_format(p.datep, '%Y%m')"; + if ($mode == 'bynumber') { $sql.= ", count(DISTINCT p.rowid)"; + } + $sql.= " FROM ".MAIN_DB_PREFIX."propaldet as d, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."societe as s"; + if ($filteronproducttype >= 0) { $sql.=", ".MAIN_DB_PREFIX."product as prod"; + } + if (!$user->rights->societe->client->voir && !$socid) { $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE p.rowid = d.fk_propal"; + if ($this->id > 0) { $sql.= " AND d.fk_product =".$this->id; + } else { $sql.=" AND d.fk_product > 0"; + } + if ($filteronproducttype >= 0) { $sql.= " AND prod.rowid = d.fk_product AND prod.fk_product_type =".$filteronproducttype; + } + $sql.= " AND p.fk_soc = s.rowid"; + $sql.= " AND p.entity IN (".getEntity('propal').")"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND p.fk_soc = ".$socid; + } + $sql.=$morefilter; + $sql.= " GROUP BY date_format(p.datep,'%Y%m')"; + $sql.= " ORDER BY date_format(p.datep,'%Y%m') DESC"; + + return $this->_get_stats($sql, $mode, $year); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return nb of units or proposals in which product is included + * + * @param int $socid Limit count on a particular third party id + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only + * @param int $year Year (0=last 12 month) + * @param string $morefilter More sql filters + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function get_nb_propalsupplier($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') + { + // phpcs:enable + global $conf; + global $user; + + $sql = "SELECT sum(d.qty), date_format(p.date_valid, '%Y%m')"; + if ($mode == 'bynumber') { $sql.= ", count(DISTINCT p.rowid)"; + } + $sql.= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."societe as s"; + if ($filteronproducttype >= 0) { $sql.=", ".MAIN_DB_PREFIX."product as prod"; + } + if (!$user->rights->societe->client->voir && !$socid) { $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE p.rowid = d.fk_supplier_proposal"; + if ($this->id > 0) { $sql.= " AND d.fk_product =".$this->id; + } else { $sql.=" AND d.fk_product > 0"; + } + if ($filteronproducttype >= 0) { $sql.= " AND prod.rowid = d.fk_product AND prod.fk_product_type =".$filteronproducttype; + } + $sql.= " AND p.fk_soc = s.rowid"; + $sql.= " AND p.entity IN (".getEntity('supplier_proposal').")"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND p.fk_soc = ".$socid; + } + $sql.=$morefilter; + $sql.= " GROUP BY date_format(p.date_valid,'%Y%m')"; + $sql.= " ORDER BY date_format(p.date_valid,'%Y%m') DESC"; + + return $this->_get_stats($sql, $mode, $year); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return nb of units or orders in which product is included + * + * @param int $socid Limit count on a particular third party id + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only + * @param int $year Year (0=last 12 month) + * @param string $morefilter More sql filters + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function get_nb_order($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') + { + // phpcs:enable + global $conf, $user; + + $sql = "SELECT sum(d.qty), date_format(c.date_commande, '%Y%m')"; + if ($mode == 'bynumber') { $sql.= ", count(DISTINCT c.rowid)"; + } + $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as d, ".MAIN_DB_PREFIX."commande as c, ".MAIN_DB_PREFIX."societe as s"; + if ($filteronproducttype >= 0) { $sql.=", ".MAIN_DB_PREFIX."product as p"; + } + if (!$user->rights->societe->client->voir && !$socid) { $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE c.rowid = d.fk_commande"; + if ($this->id > 0) { $sql.= " AND d.fk_product =".$this->id; + } else { $sql.=" AND d.fk_product > 0"; + } + if ($filteronproducttype >= 0) { $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; + } + $sql.= " AND c.fk_soc = s.rowid"; + $sql.= " AND c.entity IN (".getEntity('commande').")"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND c.fk_soc = ".$socid; + } + $sql.=$morefilter; + $sql.= " GROUP BY date_format(c.date_commande,'%Y%m')"; + $sql.= " ORDER BY date_format(c.date_commande,'%Y%m') DESC"; + + return $this->_get_stats($sql, $mode, $year); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return nb of units or orders in which product is included + * + * @param int $socid Limit count on a particular third party id + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only + * @param int $year Year (0=last 12 month) + * @param string $morefilter More sql filters + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function get_nb_ordersupplier($socid, $mode, $filteronproducttype=-1, $year=0, $morefilter='') + { + // phpcs:enable + global $conf, $user; + + $sql = "SELECT sum(d.qty), date_format(c.date_commande, '%Y%m')"; + if ($mode == 'bynumber') { $sql.= ", count(DISTINCT c.rowid)"; + } + $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as d, ".MAIN_DB_PREFIX."commande_fournisseur as c, ".MAIN_DB_PREFIX."societe as s"; + if ($filteronproducttype >= 0) { $sql.=", ".MAIN_DB_PREFIX."product as p"; + } + if (!$user->rights->societe->client->voir && !$socid) { $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql.= " WHERE c.rowid = d.fk_commande"; + if ($this->id > 0) { $sql.= " AND d.fk_product =".$this->id; + } else { $sql.=" AND d.fk_product > 0"; + } + if ($filteronproducttype >= 0) { $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; + } + $sql.= " AND c.fk_soc = s.rowid"; + $sql.= " AND c.entity IN (".getEntity('supplier_order').")"; + if (!$user->rights->societe->client->voir && !$socid) { $sql.= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($socid > 0) { $sql.= " AND c.fk_soc = ".$socid; + } + $sql.=$morefilter; + $sql.= " GROUP BY date_format(c.date_commande,'%Y%m')"; + $sql.= " ORDER BY date_format(c.date_commande,'%Y%m') DESC"; + + return $this->_get_stats($sql, $mode, $year); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Link a product/service to a parent product/service + * + * @param int $id_pere Id of parent product/service + * @param int $id_fils Id of child product/service + * @param int $qty Quantity + * @param int $incdec 1=Increase/decrease stock of child when parent stock increase/decrease + * @return int < 0 if KO, > 0 if OK + */ + function add_sousproduit($id_pere, $id_fils, $qty, $incdec=1) + { + // phpcs:enable + // Clean parameters + if (! is_numeric($id_pere)) { $id_pere=0; + } + if (! is_numeric($id_fils)) { $id_fils=0; + } + if (! is_numeric($incdec)) { $incdec=0; + } + + $result=$this->del_sousproduit($id_pere, $id_fils); + if ($result < 0) { return $result; + } + + // Check not already father of id_pere (to avoid father -> child -> father links) + $sql = 'SELECT fk_product_pere from '.MAIN_DB_PREFIX.'product_association'; + $sql .= ' WHERE fk_product_pere = '.$id_fils.' AND fk_product_fils = '.$id_pere; + if (! $this->db->query($sql)) { + dol_print_error($this->db); + return -1; + } + else + { + $result = $this->db->query($sql); + if ($result) { + $num = $this->db->num_rows($result); + if($num > 0) { + $this->error="isFatherOfThis"; + return -1; + } + else + { + $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_association(fk_product_pere,fk_product_fils,qty,incdec)'; + $sql .= ' VALUES ('.$id_pere.', '.$id_fils.', '.$qty.', '.$incdec.')'; + if (! $this->db->query($sql)) { + dol_print_error($this->db); + return -1; + } + else + { + return 1; + } + } + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Modify composed product + * + * @param int $id_pere Id of parent product/service + * @param int $id_fils Id of child product/service + * @param int $qty Quantity + * @param int $incdec 1=Increase/decrease stock of child when parent stock increase/decrease + * @return int < 0 if KO, > 0 if OK + */ + function update_sousproduit($id_pere, $id_fils, $qty, $incdec=1) + { + // phpcs:enable + // Clean parameters + if (! is_numeric($id_pere)) { $id_pere=0; + } + if (! is_numeric($id_fils)) { $id_fils=0; + } + if (! is_numeric($incdec)) { $incdec=1; + } + if (! is_numeric($qty)) { $qty=1; + } + + $sql = 'UPDATE '.MAIN_DB_PREFIX.'product_association SET '; + $sql.= 'qty='.$qty; + $sql.= ',incdec='.$incdec; + $sql .= ' WHERE fk_product_pere='.$id_pere.' AND fk_product_fils='.$id_fils; + + if (!$this->db->query($sql)) { + dol_print_error($this->db); + return -1; + } + else + { + return 1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Retire le lien entre un sousproduit et un produit/service + * + * @param int $fk_parent Id du produit auquel ne sera plus lie le produit lie + * @param int $fk_child Id du produit a ne plus lie + * @return int < 0 if KO, > 0 if OK + */ + function del_sousproduit($fk_parent, $fk_child) + { + // phpcs:enable + if (! is_numeric($fk_parent)) { $fk_parent=0; + } + if (! is_numeric($fk_child)) { $fk_child=0; + } + + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_association"; + $sql.= " WHERE fk_product_pere = ".$fk_parent; + $sql.= " AND fk_product_fils = ".$fk_child; + + dol_syslog(get_class($this).'::del_sousproduit', LOG_DEBUG); + if (! $this->db->query($sql)) { + dol_print_error($this->db); + return -1; + } + + return 1; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Verifie si c'est un sous-produit + * + * @param int $fk_parent Id du produit auquel le produit est lie + * @param int $fk_child Id du produit lie + * @return int < 0 si erreur, > 0 si ok + */ + function is_sousproduit($fk_parent, $fk_child) + { + // phpcs:enable + $sql = "SELECT fk_product_pere, qty, incdec"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_association"; + $sql.= " WHERE fk_product_pere = '".$fk_parent."'"; + $sql.= " AND fk_product_fils = '".$fk_child."'"; + + $result = $this->db->query($sql); + if ($result) { + $num = $this->db->num_rows($result); + + if($num > 0) { + $obj = $this->db->fetch_object($result); + $this->is_sousproduit_qty = $obj->qty; + $this->is_sousproduit_incdec = $obj->incdec; + + return true; + } + else + { + return false; + } + } + else + { + dol_print_error($this->db); + return -1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Add a supplier price for the product. + * Note: Duplicate ref is accepted for different quantity only, or for different companies. + * + * @param User $user User that make link + * @param int $id_fourn Supplier id + * @param string $ref_fourn Supplier ref + * @param float $quantity Quantity minimum for price + * @return int < 0 if KO, 0 if link already exists for this product, > 0 if OK + */ + function add_fournisseur($user, $id_fourn, $ref_fourn, $quantity) + { + // phpcs:enable + global $conf; + + $now=dol_now(); + + dol_syslog(get_class($this)."::add_fournisseur id_fourn = ".$id_fourn." ref_fourn=".$ref_fourn." quantity=".$quantity, LOG_DEBUG); + + if ($ref_fourn) { + $sql = "SELECT rowid, fk_product"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; + $sql.= " WHERE fk_soc = ".$id_fourn; + $sql.= " AND ref_fourn = '".$this->db->escape($ref_fourn)."'"; + $sql.= " AND fk_product != ".$this->id; + $sql.= " AND entity IN (".getEntity('productsupplierprice').")"; + + $resql=$this->db->query($sql); + if ($resql) { + $obj = $this->db->fetch_object($resql); + if ($obj) { + // If the supplier ref already exists but for another product (duplicate ref is accepted for different quantity only or different companies) + $this->product_id_already_linked = $obj->fk_product; + return -3; + } + $this->db->free($resql); + } + } + + $sql = "SELECT rowid"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; + $sql.= " WHERE fk_soc = ".$id_fourn; + if ($ref_fourn) { $sql.= " AND ref_fourn = '".$this->db->escape($ref_fourn)."'"; + } else { $sql.= " AND (ref_fourn = '' OR ref_fourn IS NULL)"; + } + $sql.= " AND quantity = '".$quantity."'"; + $sql.= " AND fk_product = ".$this->id; + $sql.= " AND entity IN (".getEntity('productsupplierprice').")"; + + $resql=$this->db->query($sql); + if ($resql) { + $obj = $this->db->fetch_object($resql); + + // The reference supplier does not exist, we create it for this product. + if (! $obj) { + $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_fournisseur_price("; + $sql.= "datec"; + $sql.= ", entity"; + $sql.= ", fk_product"; + $sql.= ", fk_soc"; + $sql.= ", ref_fourn"; + $sql.= ", quantity"; + $sql.= ", fk_user"; + $sql.= ", tva_tx"; + $sql.= ") VALUES ("; + $sql.= "'".$this->db->idate($now)."'"; + $sql.= ", ".$conf->entity; + $sql.= ", ".$this->id; + $sql.= ", ".$id_fourn; + $sql.= ", '".$this->db->escape($ref_fourn)."'"; + $sql.= ", ".$quantity; + $sql.= ", ".$user->id; + $sql.= ", 0"; + $sql.= ")"; + + if ($this->db->query($sql)) { + $this->product_fourn_price_id = $this->db->last_insert_id(MAIN_DB_PREFIX."product_fournisseur_price"); + return 1; + } + else + { + $this->error=$this->db->lasterror(); + return -1; + } + } + // If the supplier price already exists for this product and quantity + else + { + $this->product_fourn_price_id = $obj->rowid; + return 0; + } + } + else + { + $this->error=$this->db->lasterror(); + return -2; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Renvoie la liste des fournisseurs du produit/service + * + * @return array Tableau des id de fournisseur + */ + function list_suppliers() + { + // phpcs:enable + global $conf; + + $list = array(); + + $sql = "SELECT DISTINCT p.fk_soc"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as p"; + $sql.= " WHERE p.fk_product = ".$this->id; + $sql.= " AND p.entity = ".$conf->entity; + + $result = $this->db->query($sql); + if ($result) { + $num = $this->db->num_rows($result); + $i=0; + while ($i < $num) + { + $obj = $this->db->fetch_object($result); + $list[$i] = $obj->fk_soc; + $i++; + } + } + + return $list; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Recopie les prix d'un produit/service sur un autre + * + * @param int $fromId Id product source + * @param int $toId Id product target + * @return int < 0 if KO, > 0 if OK + */ + function clone_price($fromId, $toId) + { + // phpcs:enable + $this->db->begin(); + + // les prix + $sql = "INSERT ".MAIN_DB_PREFIX."product_price ("; + $sql.= " fk_product, date_price, price, tva_tx, localtax1_tx, localtax2_tx, fk_user_author, tosell)"; + $sql.= " SELECT ".$toId . ", date_price, price, tva_tx, localtax1_tx, localtax2_tx, fk_user_author, tosell"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_price "; + $sql.= " WHERE fk_product = ". $fromId; + + dol_syslog(get_class($this).'::clone_price', LOG_DEBUG); + if (! $this->db->query($sql)) { + $this->db->rollback(); + return -1; + } + $this->db->commit(); + return 1; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Clone links between products + * + * @param int $fromId Product id + * @param int $toId Product id + * @return int <0 if KO, >0 if OK + */ + function clone_associations($fromId, $toId) + { + // phpcs:enable + $this->db->begin(); + + $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_association (fk_product_pere, fk_product_fils, qty)'; + $sql.= " SELECT ".$toId.", fk_product_fils, qty FROM ".MAIN_DB_PREFIX."product_association"; + $sql.= " WHERE fk_product_pere = ".$fromId; + + dol_syslog(get_class($this).'::clone_association', LOG_DEBUG); + if (! $this->db->query($sql)) { + $this->db->rollback(); + return -1; + } + + $this->db->commit(); + return 1; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Recopie les fournisseurs et prix fournisseurs d'un produit/service sur un autre + * + * @param int $fromId Id produit source + * @param int $toId Id produit cible + * @return int < 0 si erreur, > 0 si ok + */ + function clone_fournisseurs($fromId, $toId) + { + // phpcs:enable + $this->db->begin(); + + $now=dol_now(); + + // les fournisseurs + /*$sql = "INSERT ".MAIN_DB_PREFIX."product_fournisseur (" + . " datec, fk_product, fk_soc, ref_fourn, fk_user_author )" + . " SELECT '".$this->db->idate($now)."', ".$toId.", fk_soc, ref_fourn, fk_user_author" + . " FROM ".MAIN_DB_PREFIX."product_fournisseur" + . " WHERE fk_product = ".$fromId; + + if ( ! $this->db->query($sql ) ) + { + $this->db->rollback(); + return -1; + }*/ + + // les prix de fournisseurs. + $sql = "INSERT ".MAIN_DB_PREFIX."product_fournisseur_price ("; + $sql.= " datec, fk_product, fk_soc, price, quantity, fk_user)"; + $sql.= " SELECT '".$this->db->idate($now)."', ".$toId. ", fk_soc, price, quantity, fk_user"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; + $sql.= " WHERE fk_product = ".$fromId; + + dol_syslog(get_class($this).'::clone_fournisseurs', LOG_DEBUG); + $resql=$this->db->query($sql); + if (! $resql) { + $this->db->rollback(); + return -1; + } + else + { + $this->db->commit(); + return 1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Fonction recursive uniquement utilisee par get_arbo_each_prod, recompose l'arborescence des sousproduits + * Define value of this->res + * + * @param array $prod Products array + * @param string $compl_path Directory path of parents to add before + * @param int $multiply Because each sublevel must be multiplicated by parent nb + * @param int $level Init level + * @param int $id_parent Id parent + * @return void + */ + function fetch_prod_arbo($prod, $compl_path="", $multiply=1, $level=1, $id_parent=0) + { + // phpcs:enable + global $conf,$langs; + + $product = new Product($this->db); + //var_dump($prod); + foreach($prod as $id_product => $desc_pere) // $id_product is 0 (first call starting with root top) or an id of a sub_product + { + if (is_array($desc_pere)) // If desc_pere is an array, this means it's a child + { + $id=(! empty($desc_pere[0]) ? $desc_pere[0] :''); + $nb=(! empty($desc_pere[1]) ? $desc_pere[1] :''); + $type=(! empty($desc_pere[2]) ? $desc_pere[2] :''); + $label=(! empty($desc_pere[3]) ? $desc_pere[3] :''); + $incdec=!empty($desc_pere[4]) ? $desc_pere[4] : 0; + + if ($multiply < 1) { $multiply=1; + } + + //print "XXX We add id=".$id." - label=".$label." - nb=".$nb." - multiply=".$multiply." fullpath=".$compl_path.$label."\n"; + $this->fetch($id); // Load product + $this->load_stock('nobatch,novirtual'); // Load stock to get true this->stock_reel + $this->res[]= array( + 'id'=>$id, // Id product + 'id_parent'=>$id_parent, + 'ref'=>$this->ref, // Ref product + 'nb'=>$nb, // Nb of units that compose parent product + 'nb_total'=>$nb*$multiply, // Nb of units for all nb of product + 'stock'=>$this->stock_reel, // Stock + 'stock_alert'=>$this->seuil_stock_alerte, // Stock alert + 'label'=>$label, + 'fullpath'=>$compl_path.$label, // Label + 'type'=>$type, // Nb of units that compose parent product + 'desiredstock'=>$this->desiredstock, + 'level'=>$level, + 'incdec'=>$incdec, + 'entity'=>$this->entity + ); + + // Recursive call if there is childs to child + if (is_array($desc_pere['childs'])) { + //print 'YYY We go down for '.$desc_pere[3]." -> \n"; + $this->fetch_prod_arbo($desc_pere['childs'], $compl_path.$desc_pere[3]." -> ", $desc_pere[1]*$multiply, $level+1, $id); + } + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Build the tree of subproducts into an array + * this->sousprods is loaded by this->get_sousproduits_arbo() + * + * @param int $multiply Because each sublevel must be multiplicated by parent nb + * @return array $this->res + */ + function get_arbo_each_prod($multiply=1) + { + // phpcs:enable + $this->res = array(); + if (isset($this->sousprods) && is_array($this->sousprods)) { + foreach($this->sousprods as $prod_name => $desc_product) + { + if (is_array($desc_product)) { $this->fetch_prod_arbo($desc_product, "", $multiply, 1, $this->id); + } + } + } + //var_dump($this->res); + return $this->res; + } + + /** + * Return all parent products for current product (first level only) + * + * @return int Nb of father + child + */ + public function hasFatherOrChild() + { + $nb = 0; + + $sql = "SELECT COUNT(pa.rowid) as nb"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_association as pa"; + $sql.= " WHERE pa.fk_product_fils = ".$this->id." OR pa.fk_product_pere = ".$this->id; + $resql = $this->db->query($sql); + if ($resql) { + $obj = $this->db->fetch_object($resql); + if ($obj) { $nb = $obj->nb; + } + } + else + { + return -1; + } + + return $nb; + } + + /** + * Return if a product has variants or not + * + * @return int Number of variants + */ + public function hasVariants() + { + $nb = 0; + $sql = "SELECT count(rowid) as nb FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_parent = ".$this->id; + $sql.= " AND entity IN (".getEntity('product').")"; + + $resql = $this->db->query($sql); + if ($resql) { + $obj = $this->db->fetch_object($resql); + if ($obj) { $nb = $obj->nb; + } + } + + return $nb; + } + + + /** + * Return if loaded product is a variant + * + * @return int + */ + public function isVariant() + { + global $conf; + if (!empty($conf->variants->enabled)) { + $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "product_attribute_combination WHERE fk_product_child = " . $this->id . " AND entity IN (" . getEntity('product') . ")"; + + $query = $this->db->query($sql); + + if ($query) { + if (!$this->db->num_rows($query)) { + return false; + } + return true; + } else { + dol_print_error($this->db); + return -1; + } + } else { + return false; + } + } + + /** + * Return all parent products for current product (first level only) + * + * @return array Array of product + */ + public function getFather() + { + $sql = "SELECT p.rowid, p.label as label, p.ref as ref, pa.fk_product_pere as id, p.fk_product_type, pa.qty, pa.incdec, p.entity"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_association as pa,"; + $sql.= " ".MAIN_DB_PREFIX."product as p"; + $sql.= " WHERE p.rowid = pa.fk_product_pere"; + $sql.= " AND pa.fk_product_fils = ".$this->id; + + $res = $this->db->query($sql); + if ($res) { + $prods = array (); + while ($record = $this->db->fetch_array($res)) + { + // $record['id'] = $record['rowid'] = id of father + $prods[$record['id']]['id'] = $record['rowid']; + $prods[$record['id']]['ref'] = $record['ref']; + $prods[$record['id']]['label'] = $record['label']; + $prods[$record['id']]['qty'] = $record['qty']; + $prods[$record['id']]['incdec'] = $record['incdec']; + $prods[$record['id']]['fk_product_type'] = $record['fk_product_type']; + $prods[$record['id']]['entity'] = $record['entity']; + } + return $prods; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + + /** + * Return childs of product $id + * + * @param int $id Id of product to search childs of + * @param int $firstlevelonly Return only direct child + * @param int $level Level of recursing call (start to 1) + * @return array Return array(prodid=>array(0=prodid, 1=>qty, 2=> ...) + */ + public function getChildsArbo($id, $firstlevelonly=0, $level=1) + { + global $alreadyfound; + + $sql = "SELECT p.rowid, p.label as label, pa.qty as qty, pa.fk_product_fils as id, p.fk_product_type, pa.incdec"; + $sql.= " FROM ".MAIN_DB_PREFIX."product as p"; + $sql.= ", ".MAIN_DB_PREFIX."product_association as pa"; + $sql.= " WHERE p.rowid = pa.fk_product_fils"; + $sql.= " AND pa.fk_product_pere = ".$id; + $sql.= " AND pa.fk_product_fils != ".$id; // This should not happens, it is to avoid infinite loop if it happens + + dol_syslog(get_class($this).'::getChildsArbo id='.$id.' level='.$level, LOG_DEBUG); + + if ($level == 1) { $alreadyfound=array($id=>1); // We init array of found object to start of tree, so if we found it later (should not happened), we stop immediatly + } + // Protection against infinite loop + if ($level > 30) { return array(); + } + + $res = $this->db->query($sql); + if ($res) { + $prods = array(); + while ($rec = $this->db->fetch_array($res)) + { + if (! empty($alreadyfound[$rec['rowid']])) { + dol_syslog(get_class($this).'::getChildsArbo the product id='.$rec['rowid'].' was already found at a higher level in tree. We discard to avoid infinite loop', LOG_WARNING); + continue; + } + $alreadyfound[$rec['rowid']]=1; + $prods[$rec['rowid']]= array( + 0=>$rec['rowid'], + 1=>$rec['qty'], + 2=>$rec['fk_product_type'], + 3=>$this->db->escape($rec['label']), + 4=>$rec['incdec'] + ); + //$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty'],2=>$rec['fk_product_type']); + //$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty']); + if (empty($firstlevelonly)) { + $listofchilds=$this->getChildsArbo($rec['rowid'], 0, $level + 1); + foreach($listofchilds as $keyChild => $valueChild) + { + $prods[$rec['rowid']]['childs'][$keyChild] = $valueChild; + } + } + } + + return $prods; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return tree of all subproducts for product. Tree contains id, name and quantity. + * Set this->sousprods + * + * @return void + */ + function get_sousproduits_arbo() + { + // phpcs:enable + $parent=array(); + + foreach($this->getChildsArbo($this->id) as $keyChild => $valueChild) // Warning. getChildsArbo can call getChildsArbo recursively. Starting point is $value[0]=id of product + { + $parent[$this->label][$keyChild] = $valueChild; + } + foreach($parent as $key => $value) // key=label, value is array of childs + { + $this->sousprods[$key] = $value; + } + } + + /** + * Return clicable link of object (with eventually picto) + * + * @param int $withpicto Add picto into link + * @param string $option Where point the link ('stock', 'composition', 'category', 'supplier', '') + * @param int $maxlength Maxlength of ref + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string String with URL + */ + public function getNomUrl($withpicto=0, $option='', $maxlength=0, $save_lastsearch_value=-1) + { + global $conf, $langs, $hookmanager; + include_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; + + $result=''; + $newref=$this->ref; + if ($maxlength) { $newref=dol_trunc($newref, $maxlength, 'middle'); + } + + if ($this->type == Product::TYPE_PRODUCT) { $label = '' . $langs->trans("ShowProduct") . ''; + } + if ($this->type == Product::TYPE_SERVICE) { $label = '' . $langs->trans("ShowService") . ''; + } + if (! empty($this->ref)) { + $label .= '
' . $langs->trans('ProductRef') . ': ' . $this->ref; + } + if (! empty($this->label)) { + $label .= '
' . $langs->trans('ProductLabel') . ': ' . $this->label; + } + + if ($this->type == Product::TYPE_PRODUCT) { + if ($this->weight) { $label.="
".$langs->trans("Weight").': '.$this->weight.' '.measuring_units_string($this->weight_units, "weight"); + } + if ($this->length) { $label.="
".$langs->trans("Length").': '.$this->length.' '.measuring_units_string($this->length_units, 'length'); + } + if ($this->surface) { $label.="
".$langs->trans("Surface").': '.$this->surface.' '.measuring_units_string($this->surface_units, 'surface'); + } + if ($this->volume) { $label.="
".$langs->trans("Volume").': '.$this->volume.' '.measuring_units_string($this->volume_units, 'volume'); + } + } + + if ($this->type == Product::TYPE_PRODUCT || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) { + if (! empty($conf->productbatch->enabled)) { + $langs->load("productbatch"); + $label.="
".$langs->trans("ManageLotSerial").': '.$this->getLibStatut(0, 2); } } //if ($this->type == Product::TYPE_SERVICE) //{ // //} - if (! empty($conf->accounting->enabled) && $this->status) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; - $label.= '
' . $langs->trans('ProductAccountancySellCode') . ': '. length_accountg($this->accountancy_code_sell); - $label.= '
' . $langs->trans('ProductAccountancySellIntraCode') . ': '. length_accountg($this->accountancy_code_sell_export); - $label.= '
' . $langs->trans('ProductAccountancySellExportCode') . ': '. length_accountg($this->accountancy_code_sell_intra); + if (! empty($conf->accounting->enabled) && $this->status) { + include_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; + $label.= '
' . $langs->trans('ProductAccountancySellCode') . ': '. length_accountg($this->accountancy_code_sell); + $label.= '
' . $langs->trans('ProductAccountancySellIntraCode') . ': '. length_accountg($this->accountancy_code_sell_export); + $label.= '
' . $langs->trans('ProductAccountancySellExportCode') . ': '. length_accountg($this->accountancy_code_sell_intra); } - if (! empty($conf->accounting->enabled) && $this->status_buy) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; - $label.= '
' . $langs->trans('ProductAccountancyBuyCode') . ': '. length_accountg($this->accountancy_code_buy); + if (! empty($conf->accounting->enabled) && $this->status_buy) { + include_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; + $label.= '
' . $langs->trans('ProductAccountancyBuyCode') . ': '. length_accountg($this->accountancy_code_buy); } - if (! empty($this->entity)) - { + if (! empty($this->entity)) { $tmpphoto = $this->show_photos('product', $conf->product->multidir_output[$this->entity], 1, 1, 0, 0, 0, 80); - if ($this->nbphoto > 0) $label .= '
' . $tmpphoto; + if ($this->nbphoto > 0) { $label .= '
' . $tmpphoto; + } } - $linkclose=''; - if (empty($notooltip)) - { - if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) - { - $label=$langs->trans("ShowOrder"); - $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; - } + $linkclose=''; + if (empty($notooltip)) { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { + $label=$langs->trans("ShowOrder"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } - $linkclose.= ' title="'.dol_escape_htmltag($label, 1, 1).'"'; - $linkclose.= ' class="classfortooltip"'; + $linkclose.= ' title="'.dol_escape_htmltag($label, 1, 1).'"'; + $linkclose.= ' class="classfortooltip"'; - /* - $hookmanager->initHooks(array('productdao')); - $parameters=array('id'=>$this->id); - $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - if ($reshook > 0) $linkclose = $hookmanager->resPrint; - */ - } + /* + $hookmanager->initHooks(array('productdao')); + $parameters=array('id'=>$this->id); + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $linkclose = $hookmanager->resPrint; + */ + } if ($option == 'supplier' || $option == 'category') { $url = DOL_URL_ROOT.'/product/fournisseurs.php?id='.$this->id; @@ -3774,12 +3799,13 @@ class Product extends CommonObject $url = DOL_URL_ROOT.'/product/card.php?id='.$this->id; } - if ($option !== 'nolink') - { - // Add param to save lastsearch_values or not - $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); - if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; - if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + if ($option !== 'nolink') { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { $add_save_lastsearch_values=1; + } + if ($add_save_lastsearch_values) { $url.='&save_lastsearch_values=1'; + } } $linkstart = 'type == Product::TYPE_PRODUCT) $result.=(img_object(($notooltip?'':$label), 'product', ($notooltip?'class="paddingright"':'class="paddingright classfortooltip"'), 0, 0, $notooltip?0:1)); - if ($this->type == Product::TYPE_SERVICE) $result.=(img_object(($notooltip?'':$label), 'service', ($notooltip?'class="paddinright"':'class="paddingright classfortooltip"'), 0, 0, $notooltip?0:1)); - } - $result.= $newref; - $result.= $linkend; + if ($withpicto) { + if ($this->type == Product::TYPE_PRODUCT) { $result.=(img_object(($notooltip?'':$label), 'product', ($notooltip?'class="paddingright"':'class="paddingright classfortooltip"'), 0, 0, $notooltip?0:1)); + } + if ($this->type == Product::TYPE_SERVICE) { $result.=(img_object(($notooltip?'':$label), 'service', ($notooltip?'class="paddinright"':'class="paddingright classfortooltip"'), 0, 0, $notooltip?0:1)); + } + } + $result.= $newref; + $result.= $linkend; - global $action; - $hookmanager->initHooks(array('productdao')); - $parameters=array('id'=>$this->id, 'getnomurl'=>$result); - $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - if ($reshook > 0) $result = $hookmanager->resPrint; - else $result .= $hookmanager->resPrint; + global $action; + $hookmanager->initHooks(array('productdao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { $result = $hookmanager->resPrint; + } else { $result .= $hookmanager->resPrint; + } - return $result; - } + return $result; + } - /** - * Create a document onto disk according to template module. - * - * @param string $modele Force model to use ('' to not force) - * @param Translate $outputlangs Object langs to use for output - * @param int $hidedetails Hide details of lines - * @param int $hidedesc Hide description - * @param int $hideref Hide ref - * @return int 0 if KO, 1 if OK - */ - public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0) - { - global $conf,$user,$langs; - - $langs->load("products"); - - // Positionne le modele sur le nom du modele a utiliser - if (! dol_strlen($modele)) - { - if (! empty($conf->global->PRODUCT_ADDON_PDF)) - { - $modele = $conf->global->PRODUCT_ADDON_PDF; - } - else - { - $modele = 'strato'; - } - } - - $modelpath = "core/modules/product/doc/"; - - return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); - } - - /** - * Return label of status of object - * - * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto - * @param int $type 0=Sell, 1=Buy, 2=Batch Number management - * @return string Label of status - */ - public function getLibStatut($mode=0, $type=0) - { - switch ($type) - { - case 0: - return $this->LibStatut($this->status,$mode,$type); - case 1: - return $this->LibStatut($this->status_buy,$mode,$type); - case 2: - return $this->LibStatut($this->status_batch,$mode,$type); - default: - //Simulate previous behavior but should return an error string - return $this->LibStatut($this->status_buy,$mode,$type); - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return label of a given status - * - * @param int $status Statut - * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto - * @param int $type 0=Status "to sell", 1=Status "to buy", 2=Status "to Batch" - * @return string Label of status - */ - function LibStatut($status,$mode=0,$type=0) - { - // phpcs:enable - global $conf, $langs; - - $langs->load('products'); - if (! empty($conf->productbatch->enabled)) $langs->load("productbatch"); - - if ($type == 2) - { - switch ($mode) - { - case 0: - return ($status == 0 ? $langs->trans('ProductStatusNotOnBatch') : $langs->trans('ProductStatusOnBatch')); - case 1: - return ($status == 0 ? $langs->trans('ProductStatusNotOnBatchShort') : $langs->trans('ProductStatusOnBatchShort')); - case 2: - return $this->LibStatut($status,3,2).' '.$this->LibStatut($status,1,2); - case 3: - if ($status == 0) - { - return img_picto($langs->trans('ProductStatusNotOnBatch'),'statut5'); - } - return img_picto($langs->trans('ProductStatusOnBatch'),'statut4'); - case 4: - return $this->LibStatut($status,3,2).' '.$this->LibStatut($status,0,2); - case 5: - return $this->LibStatut($status,1,2).' '.$this->LibStatut($status,3,2); - default: - return $langs->trans('Unknown'); - } - } - if ($mode == 0) - { - if ($status == 0) return ($type==0 ? $langs->trans('ProductStatusNotOnSellShort'):$langs->trans('ProductStatusNotOnBuyShort')); - elseif ($status == 1) return ($type==0 ? $langs->trans('ProductStatusOnSellShort'):$langs->trans('ProductStatusOnBuyShort')); - } - elseif ($mode == 1) - { - if ($status == 0) return ($type==0 ? $langs->trans('ProductStatusNotOnSell'):$langs->trans('ProductStatusNotOnBuy')); - elseif ($status == 1) return ($type==0 ? $langs->trans('ProductStatusOnSell'):$langs->trans('ProductStatusOnBuy')); - } - elseif ($mode == 2) - { - if ($status == 0) return img_picto($langs->trans('ProductStatusNotOnSell'),'statut5', 'class="pictostatus"').' '.($type==0 ? $langs->trans('ProductStatusNotOnSellShort'):$langs->trans('ProductStatusNotOnBuyShort')); - elseif ($status == 1) return img_picto($langs->trans('ProductStatusOnSell'),'statut4', 'class="pictostatus"').' '.($type==0 ? $langs->trans('ProductStatusOnSellShort'):$langs->trans('ProductStatusOnBuyShort')); - } - elseif ($mode == 3) - { - if ($status == 0) return img_picto(($type==0 ? $langs->trans('ProductStatusNotOnSell') : $langs->trans('ProductStatusNotOnBuy')),'statut5', 'class="pictostatus"'); - elseif ($status == 1) return img_picto(($type==0 ? $langs->trans('ProductStatusOnSell') : $langs->trans('ProductStatusOnBuy')),'statut4', 'class="pictostatus"'); - } - elseif ($mode == 4) - { - if ($status == 0) return img_picto($langs->trans('ProductStatusNotOnSell'),'statut5', 'class="pictostatus"').' '.($type==0 ? $langs->trans('ProductStatusNotOnSell'):$langs->trans('ProductStatusNotOnBuy')); - elseif ($status == 1) return img_picto($langs->trans('ProductStatusOnSell'),'statut4', 'class="pictostatus"').' '.($type==0 ? $langs->trans('ProductStatusOnSell'):$langs->trans('ProductStatusOnBuy')); - } - elseif ($mode == 5) - { - if ($status == 0) return ($type==0 ? $langs->trans('ProductStatusNotOnSellShort'):$langs->trans('ProductStatusNotOnBuyShort')).' '.img_picto(($type==0 ? $langs->trans('ProductStatusNotOnSell'):$langs->trans('ProductStatusNotOnBuy')), 'statut5', 'class="pictostatus"'); - elseif ($status == 1) return ($type==0 ? $langs->trans('ProductStatusOnSellShort'):$langs->trans('ProductStatusOnBuyShort')).' '.img_picto(($type==0 ? $langs->trans('ProductStatusOnSell'):$langs->trans('ProductStatusOnBuy')),'statut4', 'class="pictostatus"'); - } - elseif ($mode == 6) - { - if ($status == 0) return ($type==0 ? $langs->trans('ProductStatusNotOnSellShort'):$langs->trans('ProductStatusNotOnBuyShort')).' '.img_picto(($type==0 ? $langs->trans('ProductStatusNotOnSell'):$langs->trans('ProductStatusNotOnBuy')), 'statut5', 'class="pictostatus"'); - elseif ($status == 1) return ($type==0 ? $langs->trans('ProductStatusOnSellShort'):$langs->trans('ProductStatusOnBuyShort')).' '.img_picto(($type==0 ? $langs->trans('ProductStatusOnSell'):$langs->trans('ProductStatusOnBuy')),'statut4', 'class="pictostatus"'); - } - return $langs->trans('Unknown'); - } - - - /** - * Retourne le libelle du finished du produit - * - * @return string Libelle - */ - function getLibFinished() - { - global $langs; - $langs->load('products'); - - if ($this->finished == '0') return $langs->trans("RowMaterial"); - if ($this->finished == '1') return $langs->trans("Finished"); - return ''; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Adjust stock in a warehouse for product - * - * @param User $user user asking change - * @param int $id_entrepot id of warehouse - * @param double $nbpiece nb of units - * @param int $movement 0 = add, 1 = remove - * @param string $label Label of stock movement - * @param double $price Unit price HT of product, used to calculate average weighted price (PMP in french). If 0, average weighted price is not changed. - * @param string $inventorycode Inventory code - * @param string $origin_element Origin element type - * @param int $origin_id Origin id of element - * @return int <0 if KO, >0 if OK - */ - function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $inventorycode='', $origin_element='', $origin_id=null) - { - // phpcs:enable - if ($id_entrepot) - { - $this->db->begin(); - - require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php'; - - $op[0] = "+".trim($nbpiece); - $op[1] = "-".trim($nbpiece); - - $movementstock=new MouvementStock($this->db); - $movementstock->setOrigin($origin_element, $origin_id); - $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label,$inventorycode); - - if ($result >= 0) - { - $this->db->commit(); - return 1; - } - else - { - $this->error=$movementstock->error; - $this->errors=$movementstock->errors; - - $this->db->rollback(); - return -1; - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Adjust stock in a warehouse for product with batch number - * - * @param User $user user asking change - * @param int $id_entrepot id of warehouse - * @param double $nbpiece nb of units - * @param int $movement 0 = add, 1 = remove - * @param string $label Label of stock movement - * @param double $price Price to use for stock eval - * @param date $dlc eat-by date - * @param date $dluo sell-by date - * @param string $lot Lot number - * @param string $inventorycode Inventory code - * @param string $origin_element Origin element type - * @param int $origin_id Origin id of element - * @return int <0 if KO, >0 if OK - */ - function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $dlc='', $dluo='',$lot='', $inventorycode='', $origin_element='', $origin_id=null) - { - // phpcs:enable - if ($id_entrepot) - { - $this->db->begin(); - - require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php'; - - $op[0] = "+".trim($nbpiece); - $op[1] = "-".trim($nbpiece); - - $movementstock=new MouvementStock($this->db); - $movementstock->setOrigin($origin_element, $origin_id); - $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label,$inventorycode,'',$dlc,$dluo,$lot); - - if ($result >= 0) - { - $this->db->commit(); - return 1; - } - else - { - $this->error=$movementstock->error; - $this->errors=$movementstock->errors; - - $this->db->rollback(); - return -1; - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Load information about stock of a product into ->stock_reel, ->stock_warehouse[] (including stock_warehouse[idwarehouse]->detail_batch for batch products) - * This function need a lot of load. If you use it on list, use a cache to execute it once for each product id. - * If ENTREPOT_EXTRA_STATUS set, filtering on warehouse status possible. - * - * @param string $option '' = Load all stock info, also from closed and internal warehouses, - * 'nobatch' = Do not load batch information, - * 'novirtual' = Do not load virtual stock, - * 'warehouseopen' = Load stock from open warehouses only, - * 'warehouseclosed' = Load stock from closed warehouses only, - * 'warehouseinternal' = Load stock from warehouses for internal correction/transfer only - * @return int < 0 if KO, > 0 if OK - * @see load_virtual_stock(), loadBatchInfo() - */ - function load_stock($option='') - { - // phpcs:enable - global $conf; - - $this->stock_reel = 0; - $this->stock_warehouse = array(); - $this->stock_theorique = 0; - - $warehouseStatus = array(); - - if (preg_match('/warehouseclosed/', $option)) - { - $warehouseStatus[] = Entrepot::STATUS_CLOSED; - } - if (preg_match('/warehouseopen/', $option)) - { - $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL; - } - if (preg_match('/warehouseinternal/', $option)) - { - $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL; - } - - $sql = "SELECT ps.rowid, ps.reel, ps.fk_entrepot"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_stock as ps"; - $sql.= ", ".MAIN_DB_PREFIX."entrepot as w"; - $sql.= " WHERE w.entity IN (".getEntity('stock').")"; - $sql.= " AND w.rowid = ps.fk_entrepot"; - $sql.= " AND ps.fk_product = ".$this->id; - if ($conf->global->ENTREPOT_EXTRA_STATUS && count($warehouseStatus)) $sql.= " AND w.statut IN (".$this->db->escape(implode(',',$warehouseStatus)).")"; - - dol_syslog(get_class($this)."::load_stock", LOG_DEBUG); - $result = $this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - $i=0; - if ($num > 0) - { - while ($i < $num) - { - $row = $this->db->fetch_object($result); - $this->stock_warehouse[$row->fk_entrepot] = new stdClass(); - $this->stock_warehouse[$row->fk_entrepot]->real = $row->reel; - $this->stock_warehouse[$row->fk_entrepot]->id = $row->rowid; - if ((! preg_match('/nobatch/', $option)) && $this->hasbatch()) $this->stock_warehouse[$row->fk_entrepot]->detail_batch=Productbatch::findAll($this->db, $row->rowid, 1, $this->id); - $this->stock_reel+=$row->reel; - $i++; - } - } - $this->db->free($result); - - if (! preg_match('/novirtual/', $option)) - { - $this->load_virtual_stock(); // This also load stats_commande_fournisseur, ... - } - - return 1; - } - else - { - $this->error=$this->db->lasterror(); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Load value ->stock_theorique of a product. Property this->id must be defined. - * This function need a lot of load. If you use it on list, use a cache to execute it one for each product id. - * - * @return int < 0 if KO, > 0 if OK - * @see load_stock(), loadBatchInfo() - */ - function load_virtual_stock() - { - // phpcs:enable - global $conf, $hookmanager, $action; - - $stock_commande_client=0; - $stock_commande_fournisseur=0; - $stock_sending_client=0; - $stock_reception_fournisseur=0; - - if (! empty($conf->commande->enabled)) - { - $result=$this->load_stats_commande(0,'1,2', 1); - if ($result < 0) dol_print_error($this->db,$this->error); - $stock_commande_client=$this->stats_commande['qty']; - } - if (! empty($conf->expedition->enabled)) - { - $result=$this->load_stats_sending(0,'1,2', 1); - if ($result < 0) dol_print_error($this->db,$this->error); - $stock_sending_client=$this->stats_expedition['qty']; - } - if (! empty($conf->fournisseur->enabled)) - { - $result=$this->load_stats_commande_fournisseur(0,'1,2,3,4', 1); - if ($result < 0) dol_print_error($this->db,$this->error); - $stock_commande_fournisseur=$this->stats_commande_fournisseur['qty']; - - $result=$this->load_stats_reception(0,'4', 1); - if ($result < 0) dol_print_error($this->db,$this->error); - $stock_reception_fournisseur=$this->stats_reception['qty']; - } - - // Stock decrease mode - if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) { - $this->stock_theorique=$this->stock_reel-$stock_commande_client+$stock_sending_client; - } - if (! empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER)) { - $this->stock_theorique=$this->stock_reel; - } - if (! empty($conf->global->STOCK_CALCULATE_ON_BILL)) { - $this->stock_theorique=$this->stock_reel-$stock_commande_client; - } - // Stock Increase mode - if (! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) { - $this->stock_theorique+=$stock_commande_fournisseur-$stock_reception_fournisseur; - } - if (! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER)) { - $this->stock_theorique-=$stock_reception_fournisseur; - } - if (! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) { - $this->stock_theorique+=$stock_commande_fournisseur-$stock_reception_fournisseur; - } - - if (! is_object($hookmanager)) { - include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; - $hookmanager=new HookManager($this->db); - } - $hookmanager->initHooks(array('productdao')); - $parameters=array('id'=>$this->id); - // Note that $action and $object may have been modified by some hooks - $reshook=$hookmanager->executeHooks('loadvirtualstock', $parameters, $this, $action); - if ($reshook > 0) $this->stock_theorique = $hookmanager->resArray['stock_theorique']; - - return 1; - } - - - /** - * Load existing information about a serial - * - * @param string $batch Lot/serial number - * @return array Array with record into product_batch - * @see load_stock(), load_virtual_stock() - */ - function loadBatchInfo($batch) + /** + * Create a document onto disk according to template module. + * + * @param string $modele Force model to use ('' to not force) + * @param Translate $outputlangs Object langs to use for output + * @param int $hidedetails Hide details of lines + * @param int $hidedesc Hide description + * @param int $hideref Hide ref + * @return int 0 if KO, 1 if OK + */ + public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0) { - $result=array(); + global $conf,$user,$langs; - $sql = "SELECT pb.batch, pb.eatby, pb.sellby, SUM(pb.qty) FROM ".MAIN_DB_PREFIX."product_batch as pb, ".MAIN_DB_PREFIX."product_stock as ps"; - $sql.= " WHERE pb.fk_product_stock = ps.rowid AND ps.fk_product = ".$this->id." AND pb.batch = '".$this->db->escape($batch)."'"; - $sql.= " GROUP BY pb.batch, pb.eatby, pb.sellby"; - dol_syslog(get_class($this)."::loadBatchInfo load first entry found for lot/serial = ".$batch, LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i=0; - while ($i < $num) - { - $obj = $this->db->fetch_object($resql); - $result[]=array('batch'=>$batch, 'eatby'=>$this->db->jdate($obj->eatby), 'sellby'=>$this->db->jdate($obj->sellby), 'qty'=>$obj->qty); - $i++; - } - return $result; - } - else - { - dol_print_error($this->db); - $this->db->rollback(); - return array(); - } + $langs->load("products"); + + // Positionne le modele sur le nom du modele a utiliser + if (! dol_strlen($modele)) { + if (! empty($conf->global->PRODUCT_ADDON_PDF)) { + $modele = $conf->global->PRODUCT_ADDON_PDF; + } + else + { + $modele = 'strato'; + } + } + + $modelpath = "core/modules/product/doc/"; + + return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + + /** + * Return label of status of object + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto + * @param int $type 0=Sell, 1=Buy, 2=Batch Number management + * @return string Label of status + */ + public function getLibStatut($mode=0, $type=0) + { + switch ($type) + { + case 0: + return $this->LibStatut($this->status, $mode, $type); + case 1: + return $this->LibStatut($this->status_buy, $mode, $type); + case 2: + return $this->LibStatut($this->status_batch, $mode, $type); + default: + //Simulate previous behavior but should return an error string + return $this->LibStatut($this->status_buy, $mode, $type); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Return label of a given status + * + * @param int $status Statut + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto + * @param int $type 0=Status "to sell", 1=Status "to buy", 2=Status "to Batch" + * @return string Label of status + */ + function LibStatut($status,$mode=0,$type=0) + { + // phpcs:enable + global $conf, $langs; + + $langs->load('products'); + if (! empty($conf->productbatch->enabled)) { $langs->load("productbatch"); + } + + if ($type == 2) { + switch ($mode) + { + case 0: + return ($status == 0 ? $langs->trans('ProductStatusNotOnBatch') : $langs->trans('ProductStatusOnBatch')); + case 1: + return ($status == 0 ? $langs->trans('ProductStatusNotOnBatchShort') : $langs->trans('ProductStatusOnBatchShort')); + case 2: + return $this->LibStatut($status, 3, 2).' '.$this->LibStatut($status, 1, 2); + case 3: + if ($status == 0) { + return img_picto($langs->trans('ProductStatusNotOnBatch'), 'statut5'); + } + return img_picto($langs->trans('ProductStatusOnBatch'), 'statut4'); + case 4: + return $this->LibStatut($status, 3, 2).' '.$this->LibStatut($status, 0, 2); + case 5: + return $this->LibStatut($status, 1, 2).' '.$this->LibStatut($status, 3, 2); + default: + return $langs->trans('Unknown'); + } + } + if ($mode == 0) { + if ($status == 0) { return ($type==0 ? $langs->trans('ProductStatusNotOnSellShort'):$langs->trans('ProductStatusNotOnBuyShort')); + } elseif ($status == 1) { return ($type==0 ? $langs->trans('ProductStatusOnSellShort'):$langs->trans('ProductStatusOnBuyShort')); + } + } + elseif ($mode == 1) { + if ($status == 0) { return ($type==0 ? $langs->trans('ProductStatusNotOnSell'):$langs->trans('ProductStatusNotOnBuy')); + } elseif ($status == 1) { return ($type==0 ? $langs->trans('ProductStatusOnSell'):$langs->trans('ProductStatusOnBuy')); + } + } + elseif ($mode == 2) { + if ($status == 0) { return img_picto($langs->trans('ProductStatusNotOnSell'), 'statut5', 'class="pictostatus"').' '.($type==0 ? $langs->trans('ProductStatusNotOnSellShort'):$langs->trans('ProductStatusNotOnBuyShort')); + } elseif ($status == 1) { return img_picto($langs->trans('ProductStatusOnSell'), 'statut4', 'class="pictostatus"').' '.($type==0 ? $langs->trans('ProductStatusOnSellShort'):$langs->trans('ProductStatusOnBuyShort')); + } + } + elseif ($mode == 3) { + if ($status == 0) { return img_picto(($type==0 ? $langs->trans('ProductStatusNotOnSell') : $langs->trans('ProductStatusNotOnBuy')), 'statut5', 'class="pictostatus"'); + } elseif ($status == 1) { return img_picto(($type==0 ? $langs->trans('ProductStatusOnSell') : $langs->trans('ProductStatusOnBuy')), 'statut4', 'class="pictostatus"'); + } + } + elseif ($mode == 4) { + if ($status == 0) { return img_picto($langs->trans('ProductStatusNotOnSell'), 'statut5', 'class="pictostatus"').' '.($type==0 ? $langs->trans('ProductStatusNotOnSell'):$langs->trans('ProductStatusNotOnBuy')); + } elseif ($status == 1) { return img_picto($langs->trans('ProductStatusOnSell'), 'statut4', 'class="pictostatus"').' '.($type==0 ? $langs->trans('ProductStatusOnSell'):$langs->trans('ProductStatusOnBuy')); + } + } + elseif ($mode == 5) { + if ($status == 0) { return ($type==0 ? $langs->trans('ProductStatusNotOnSellShort'):$langs->trans('ProductStatusNotOnBuyShort')).' '.img_picto(($type==0 ? $langs->trans('ProductStatusNotOnSell'):$langs->trans('ProductStatusNotOnBuy')), 'statut5', 'class="pictostatus"'); + } elseif ($status == 1) { return ($type==0 ? $langs->trans('ProductStatusOnSellShort'):$langs->trans('ProductStatusOnBuyShort')).' '.img_picto(($type==0 ? $langs->trans('ProductStatusOnSell'):$langs->trans('ProductStatusOnBuy')), 'statut4', 'class="pictostatus"'); + } + } + elseif ($mode == 6) { + if ($status == 0) { return ($type==0 ? $langs->trans('ProductStatusNotOnSellShort'):$langs->trans('ProductStatusNotOnBuyShort')).' '.img_picto(($type==0 ? $langs->trans('ProductStatusNotOnSell'):$langs->trans('ProductStatusNotOnBuy')), 'statut5', 'class="pictostatus"'); + } elseif ($status == 1) { return ($type==0 ? $langs->trans('ProductStatusOnSellShort'):$langs->trans('ProductStatusOnBuyShort')).' '.img_picto(($type==0 ? $langs->trans('ProductStatusOnSell'):$langs->trans('ProductStatusOnBuy')), 'statut4', 'class="pictostatus"'); + } + } + return $langs->trans('Unknown'); + } + + + /** + * Retourne le libelle du finished du produit + * + * @return string Libelle + */ + function getLibFinished() + { + global $langs; + $langs->load('products'); + + if ($this->finished == '0') { return $langs->trans("RowMaterial"); + } + if ($this->finished == '1') { return $langs->trans("Finished"); + } + return ''; } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Move an uploaded file described into $file array into target directory $sdir. - * - * @param string $sdir Target directory - * @param string $file Array of file info of file to upload: array('name'=>..., 'tmp_name'=>...) - * @return int <0 if KO, >0 if OK - */ - function add_photo($sdir, $file) - { + /** + * Adjust stock in a warehouse for product + * + * @param User $user user asking change + * @param int $id_entrepot id of warehouse + * @param double $nbpiece nb of units + * @param int $movement 0 = add, 1 = remove + * @param string $label Label of stock movement + * @param double $price Unit price HT of product, used to calculate average weighted price (PMP in french). If 0, average weighted price is not changed. + * @param string $inventorycode Inventory code + * @param string $origin_element Origin element type + * @param int $origin_id Origin id of element + * @return int <0 if KO, >0 if OK + */ + function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $inventorycode='', $origin_element='', $origin_id=null) + { // phpcs:enable - global $conf; + if ($id_entrepot) { + $this->db->begin(); - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + include_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php'; - $result = 0; + $op[0] = "+".trim($nbpiece); + $op[1] = "-".trim($nbpiece); - $dir = $sdir; - if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $dir .= '/'. get_exdir($this->id,2,0,0,$this,'product') . $this->id ."/photos"; - else $dir .= '/'.get_exdir(0,0,0,0,$this,'product').dol_sanitizeFileName($this->ref); + $movementstock=new MouvementStock($this->db); + $movementstock->setOrigin($origin_element, $origin_id); + $result=$movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode); - dol_mkdir($dir); + if ($result >= 0) { + $this->db->commit(); + return 1; + } + else + { + $this->error=$movementstock->error; + $this->errors=$movementstock->errors; - $dir_osencoded=$dir; - - if (is_dir($dir_osencoded)) - { - $originImage = $dir . '/' . $file['name']; - - // Cree fichier en taille origine - $result=dol_move_uploaded_file($file['tmp_name'], $originImage, 1); - - if (file_exists(dol_osencode($originImage))) - { - // Create thumbs - $this->addThumbs($originImage); - } - } - - if (is_numeric($result) && $result > 0) return 1; - else return -1; - } + $this->db->rollback(); + return -1; + } + } + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Return if at least one photo is available - * - * @param string $sdir Directory to scan - * @return boolean True if at least one photo is available, False if not - */ - function is_photo_available($sdir) - { + /** + * Adjust stock in a warehouse for product with batch number + * + * @param User $user user asking change + * @param int $id_entrepot id of warehouse + * @param double $nbpiece nb of units + * @param int $movement 0 = add, 1 = remove + * @param string $label Label of stock movement + * @param double $price Price to use for stock eval + * @param date $dlc eat-by date + * @param date $dluo sell-by date + * @param string $lot Lot number + * @param string $inventorycode Inventory code + * @param string $origin_element Origin element type + * @param int $origin_id Origin id of element + * @return int <0 if KO, >0 if OK + */ + function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $dlc='', $dluo='',$lot='', $inventorycode='', $origin_element='', $origin_id=null) + { // phpcs:enable - include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php'; - include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php'; + if ($id_entrepot) { + $this->db->begin(); - global $conf; + include_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php'; - $dir = $sdir; - if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $dir .= '/'. get_exdir($this->id,2,0,0,$this,'product') . $this->id ."/photos/"; - else $dir .= '/'.get_exdir(0,0,0,0,$this,'product').dol_sanitizeFileName($this->ref).'/'; + $op[0] = "+".trim($nbpiece); + $op[1] = "-".trim($nbpiece); - $nbphoto=0; + $movementstock=new MouvementStock($this->db); + $movementstock->setOrigin($origin_element, $origin_id); + $result=$movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode, '', $dlc, $dluo, $lot); - $dir_osencoded=dol_osencode($dir); - if (file_exists($dir_osencoded)) - { - $handle=opendir($dir_osencoded); - if (is_resource($handle)) - { - while (($file = readdir($handle)) !== false) - { - if (! utf8_check($file)) $file=utf8_encode($file); // To be sure data is stored in UTF8 in memory - if (dol_is_file($dir.$file) && image_format_supported($file) > 0) return true; - } - } - } - return false; - } + if ($result >= 0) { + $this->db->commit(); + return 1; + } + else + { + $this->error=$movementstock->error; + $this->errors=$movementstock->errors; + + $this->db->rollback(); + return -1; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Load information about stock of a product into ->stock_reel, ->stock_warehouse[] (including stock_warehouse[idwarehouse]->detail_batch for batch products) + * This function need a lot of load. If you use it on list, use a cache to execute it once for each product id. + * If ENTREPOT_EXTRA_STATUS set, filtering on warehouse status possible. + * + * @param string $option '' = Load all stock info, also from closed and internal warehouses, + * @return int < 0 if KO, > 0 if OK + * @see load_virtual_stock(), loadBatchInfo() + */ + function load_stock($option='') + { + // phpcs:enable + global $conf; + + $this->stock_reel = 0; + $this->stock_warehouse = array(); + $this->stock_theorique = 0; + + $warehouseStatus = array(); + + if (preg_match('/warehouseclosed/', $option)) { + $warehouseStatus[] = Entrepot::STATUS_CLOSED; + } + if (preg_match('/warehouseopen/', $option)) { + $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL; + } + if (preg_match('/warehouseinternal/', $option)) { + $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL; + } + + $sql = "SELECT ps.rowid, ps.reel, ps.fk_entrepot"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_stock as ps"; + $sql.= ", ".MAIN_DB_PREFIX."entrepot as w"; + $sql.= " WHERE w.entity IN (".getEntity('stock').")"; + $sql.= " AND w.rowid = ps.fk_entrepot"; + $sql.= " AND ps.fk_product = ".$this->id; + if ($conf->global->ENTREPOT_EXTRA_STATUS && count($warehouseStatus)) { $sql.= " AND w.statut IN (".$this->db->escape(implode(',', $warehouseStatus)).")"; + } + + dol_syslog(get_class($this)."::load_stock", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + $num = $this->db->num_rows($result); + $i=0; + if ($num > 0) { + while ($i < $num) + { + $row = $this->db->fetch_object($result); + $this->stock_warehouse[$row->fk_entrepot] = new stdClass(); + $this->stock_warehouse[$row->fk_entrepot]->real = $row->reel; + $this->stock_warehouse[$row->fk_entrepot]->id = $row->rowid; + if ((! preg_match('/nobatch/', $option)) && $this->hasbatch()) { $this->stock_warehouse[$row->fk_entrepot]->detail_batch=Productbatch::findAll($this->db, $row->rowid, 1, $this->id); + } + $this->stock_reel+=$row->reel; + $i++; + } + } + $this->db->free($result); + + if (! preg_match('/novirtual/', $option)) { + $this->load_virtual_stock(); // This also load stats_commande_fournisseur, ... + } + + return 1; + } + else + { + $this->error=$this->db->lasterror(); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Load value ->stock_theorique of a product. Property this->id must be defined. + * This function need a lot of load. If you use it on list, use a cache to execute it one for each product id. + * + * @return int < 0 if KO, > 0 if OK + * @see load_stock(), loadBatchInfo() + */ + function load_virtual_stock() + { + // phpcs:enable + global $conf, $hookmanager, $action; + + $stock_commande_client=0; + $stock_commande_fournisseur=0; + $stock_sending_client=0; + $stock_reception_fournisseur=0; + + if (! empty($conf->commande->enabled)) { + $result=$this->load_stats_commande(0, '1,2', 1); + if ($result < 0) { dol_print_error($this->db, $this->error); + } + $stock_commande_client=$this->stats_commande['qty']; + } + if (! empty($conf->expedition->enabled)) { + $result=$this->load_stats_sending(0, '1,2', 1); + if ($result < 0) { dol_print_error($this->db, $this->error); + } + $stock_sending_client=$this->stats_expedition['qty']; + } + if (! empty($conf->fournisseur->enabled)) { + $result=$this->load_stats_commande_fournisseur(0, '1,2,3,4', 1); + if ($result < 0) { dol_print_error($this->db, $this->error); + } + $stock_commande_fournisseur=$this->stats_commande_fournisseur['qty']; + + $result=$this->load_stats_reception(0, '4', 1); + if ($result < 0) { dol_print_error($this->db, $this->error); + } + $stock_reception_fournisseur=$this->stats_reception['qty']; + } + + // Stock decrease mode + if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) { + $this->stock_theorique=$this->stock_reel-$stock_commande_client+$stock_sending_client; + } + if (! empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER)) { + $this->stock_theorique=$this->stock_reel; + } + if (! empty($conf->global->STOCK_CALCULATE_ON_BILL)) { + $this->stock_theorique=$this->stock_reel-$stock_commande_client; + } + // Stock Increase mode + if (! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) { + $this->stock_theorique+=$stock_commande_fournisseur-$stock_reception_fournisseur; + } + if (! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER)) { + $this->stock_theorique-=$stock_reception_fournisseur; + } + if (! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) { + $this->stock_theorique+=$stock_commande_fournisseur-$stock_reception_fournisseur; + } + + if (! is_object($hookmanager)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('productdao')); + $parameters=array('id'=>$this->id); + // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('loadvirtualstock', $parameters, $this, $action); + if ($reshook > 0) { $this->stock_theorique = $hookmanager->resArray['stock_theorique']; + } + + return 1; + } + + + /** + * Load existing information about a serial + * + * @param string $batch Lot/serial number + * @return array Array with record into product_batch + * @see load_stock(), load_virtual_stock() + */ + function loadBatchInfo($batch) + { + $result=array(); + + $sql = "SELECT pb.batch, pb.eatby, pb.sellby, SUM(pb.qty) FROM ".MAIN_DB_PREFIX."product_batch as pb, ".MAIN_DB_PREFIX."product_stock as ps"; + $sql.= " WHERE pb.fk_product_stock = ps.rowid AND ps.fk_product = ".$this->id." AND pb.batch = '".$this->db->escape($batch)."'"; + $sql.= " GROUP BY pb.batch, pb.eatby, pb.sellby"; + dol_syslog(get_class($this)."::loadBatchInfo load first entry found for lot/serial = ".$batch, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i=0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + $result[]=array('batch'=>$batch, 'eatby'=>$this->db->jdate($obj->eatby), 'sellby'=>$this->db->jdate($obj->sellby), 'qty'=>$obj->qty); + $i++; + } + return $result; + } + else + { + dol_print_error($this->db); + $this->db->rollback(); + return array(); + } + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Retourne tableau de toutes les photos du produit - * - * @param string $dir Repertoire a scanner - * @param int $nbmax Nombre maximum de photos (0=pas de max) - * @return array Tableau de photos - */ - function liste_photos($dir,$nbmax=0) - { + /** + * Move an uploaded file described into $file array into target directory $sdir. + * + * @param string $sdir Target directory + * @param string $file Array of file info of file to upload: array('name'=>..., 'tmp_name'=>...) + * @return int <0 if KO, >0 if OK + */ + function add_photo($sdir, $file) + { // phpcs:enable - include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; + global $conf; - $nbphoto=0; - $tabobj=array(); + include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - $dir_osencoded=dol_osencode($dir); - $handle=@opendir($dir_osencoded); - if (is_resource($handle)) - { - while (($file = readdir($handle)) !== false) - { - if (! utf8_check($file)) $file=utf8_encode($file); // readdir returns ISO - if (dol_is_file($dir.$file) && image_format_supported($file) >= 0) - { - $nbphoto++; + $result = 0; - // On determine nom du fichier vignette - $photo=$file; - $photo_vignette=''; - if (preg_match('/('.$this->regeximgext.')$/i', $photo, $regs)) - { - $photo_vignette=preg_replace('/'.$regs[0].'/i', '', $photo).'_small'.$regs[0]; - } + $dir = $sdir; + if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { $dir .= '/'. get_exdir($this->id, 2, 0, 0, $this, 'product') . $this->id ."/photos"; + } else { $dir .= '/'.get_exdir(0, 0, 0, 0, $this, 'product').dol_sanitizeFileName($this->ref); + } - $dirthumb = $dir.'thumbs/'; + dol_mkdir($dir); - // Objet - $obj=array(); - $obj['photo']=$photo; - if ($photo_vignette && dol_is_file($dirthumb.$photo_vignette)) $obj['photo_vignette']='thumbs/' . $photo_vignette; - else $obj['photo_vignette']=""; + $dir_osencoded=$dir; - $tabobj[$nbphoto-1]=$obj; + if (is_dir($dir_osencoded)) { + $originImage = $dir . '/' . $file['name']; - // On continue ou on arrete de boucler ? - if ($nbmax && $nbphoto >= $nbmax) break; - } - } + // Cree fichier en taille origine + $result=dol_move_uploaded_file($file['tmp_name'], $originImage, 1); - closedir($handle); - } + if (file_exists(dol_osencode($originImage))) { + // Create thumbs + $this->addThumbs($originImage); + } + } - return $tabobj; - } + if (is_numeric($result) && $result > 0) { return 1; + } else { return -1; + } + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Efface la photo du produit et sa vignette - * - * @param string $file Chemin de l'image - * @return void - */ - function delete_photo($file) - { + /** + * Return if at least one photo is available + * + * @param string $sdir Directory to scan + * @return boolean True if at least one photo is available, False if not + */ + function is_photo_available($sdir) + { // phpcs:enable - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; + include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php'; + include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php'; + + global $conf; + + $dir = $sdir; + if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { $dir .= '/'. get_exdir($this->id, 2, 0, 0, $this, 'product') . $this->id ."/photos/"; + } else { $dir .= '/'.get_exdir(0, 0, 0, 0, $this, 'product').dol_sanitizeFileName($this->ref).'/'; + } + + $nbphoto=0; + + $dir_osencoded=dol_osencode($dir); + if (file_exists($dir_osencoded)) { + $handle=opendir($dir_osencoded); + if (is_resource($handle)) { + while (($file = readdir($handle)) !== false) + { + if (! utf8_check($file)) { $file=utf8_encode($file); // To be sure data is stored in UTF8 in memory + } + if (dol_is_file($dir.$file) && image_format_supported($file) > 0) { return true; + } + } + } + } + return false; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Retourne tableau de toutes les photos du produit + * + * @param string $dir Repertoire a scanner + * @param int $nbmax Nombre maximum de photos (0=pas de max) + * @return array Tableau de photos + */ + function liste_photos($dir,$nbmax=0) + { + // phpcs:enable + include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; + + $nbphoto=0; + $tabobj=array(); + + $dir_osencoded=dol_osencode($dir); + $handle=@opendir($dir_osencoded); + if (is_resource($handle)) { + while (($file = readdir($handle)) !== false) + { + if (! utf8_check($file)) { $file=utf8_encode($file); // readdir returns ISO + } + if (dol_is_file($dir.$file) && image_format_supported($file) >= 0) { + $nbphoto++; + + // On determine nom du fichier vignette + $photo=$file; + $photo_vignette=''; + if (preg_match('/('.$this->regeximgext.')$/i', $photo, $regs)) { + $photo_vignette=preg_replace('/'.$regs[0].'/i', '', $photo).'_small'.$regs[0]; + } + + $dirthumb = $dir.'thumbs/'; + + // Objet + $obj=array(); + $obj['photo']=$photo; + if ($photo_vignette && dol_is_file($dirthumb.$photo_vignette)) { $obj['photo_vignette']='thumbs/' . $photo_vignette; + } else { $obj['photo_vignette']=""; + } + + $tabobj[$nbphoto-1]=$obj; + + // On continue ou on arrete de boucler ? + if ($nbmax && $nbphoto >= $nbmax) { break; + } + } + } + + closedir($handle); + } + + return $tabobj; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps + /** + * Efface la photo du produit et sa vignette + * + * @param string $file Chemin de l'image + * @return void + */ + function delete_photo($file) + { + // phpcs:enable + include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; $dir = dirname($file).'/'; // Chemin du dossier contenant l'image d'origine - $dirthumb = $dir.'/thumbs/'; // Chemin du dossier contenant la vignette - $filename = preg_replace('/'.preg_quote($dir,'/').'/i','',$file); // Nom du fichier + $dirthumb = $dir.'/thumbs/'; // Chemin du dossier contenant la vignette + $filename = preg_replace('/'.preg_quote($dir, '/').'/i', '', $file); // Nom du fichier - // On efface l'image d'origine - dol_delete_file($file, 0, 0, 0, $this); // For triggers + // On efface l'image d'origine + dol_delete_file($file, 0, 0, 0, $this); // For triggers - // Si elle existe, on efface la vignette - if (preg_match('/('.$this->regeximgext.')$/i',$filename,$regs)) - { - $photo_vignette=preg_replace('/'.$regs[0].'/i','',$filename).'_small'.$regs[0]; - if (file_exists(dol_osencode($dirthumb.$photo_vignette))) - { - dol_delete_file($dirthumb.$photo_vignette); - } + // Si elle existe, on efface la vignette + if (preg_match('/('.$this->regeximgext.')$/i', $filename, $regs)) { + $photo_vignette=preg_replace('/'.$regs[0].'/i', '', $filename).'_small'.$regs[0]; + if (file_exists(dol_osencode($dirthumb.$photo_vignette))) { + dol_delete_file($dirthumb.$photo_vignette); + } - $photo_vignette=preg_replace('/'.$regs[0].'/i','',$filename).'_mini'.$regs[0]; - if (file_exists(dol_osencode($dirthumb.$photo_vignette))) - { - dol_delete_file($dirthumb.$photo_vignette); - } - } - } + $photo_vignette=preg_replace('/'.$regs[0].'/i', '', $filename).'_mini'.$regs[0]; + if (file_exists(dol_osencode($dirthumb.$photo_vignette))) { + dol_delete_file($dirthumb.$photo_vignette); + } + } + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Load size of image file - * - * @param string $file Path to file - * @return void - */ - function get_image_size($file) - { + /** + * Load size of image file + * + * @param string $file Path to file + * @return void + */ + function get_image_size($file) + { // phpcs:enable - $file_osencoded=dol_osencode($file); - $infoImg = getimagesize($file_osencoded); // Get information on image - $this->imgWidth = $infoImg[0]; // Largeur de l'image - $this->imgHeight = $infoImg[1]; // Hauteur de l'image - } + $file_osencoded=dol_osencode($file); + $infoImg = getimagesize($file_osencoded); // Get information on image + $this->imgWidth = $infoImg[0]; // Largeur de l'image + $this->imgHeight = $infoImg[1]; // Hauteur de l'image + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps - /** - * Load indicators this->nb for the dashboard - * - * @return int <0 if KO, >0 if OK - */ - function load_state_board() - { + /** + * Load indicators this->nb for the dashboard + * + * @return int <0 if KO, >0 if OK + */ + function load_state_board() + { // phpcs:enable - global $conf, $user, $hookmanager; + global $conf, $user, $hookmanager; - $this->nb=array(); + $this->nb=array(); - $sql = "SELECT count(p.rowid) as nb, fk_product_type"; - $sql.= " FROM ".MAIN_DB_PREFIX."product as p"; - $sql.= ' WHERE p.entity IN ('.getEntity($this->element, 1).')'; - // Add where from hooks - if (is_object($hookmanager)) - { - $parameters=array(); - $reshook=$hookmanager->executeHooks('printFieldListWhere',$parameters); // Note that $action and $object may have been modified by hook - $sql.=$hookmanager->resPrint; - } + $sql = "SELECT count(p.rowid) as nb, fk_product_type"; + $sql.= " FROM ".MAIN_DB_PREFIX."product as p"; + $sql.= ' WHERE p.entity IN ('.getEntity($this->element, 1).')'; + // Add where from hooks + if (is_object($hookmanager)) { + $parameters=array(); + $reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook + $sql.=$hookmanager->resPrint; + } $sql.= ' GROUP BY fk_product_type'; - $resql=$this->db->query($sql); - if ($resql) - { - while ($obj=$this->db->fetch_object($resql)) - { - if ($obj->fk_product_type == 1) $this->nb["services"]=$obj->nb; - else $this->nb["products"]=$obj->nb; - } + $resql=$this->db->query($sql); + if ($resql) { + while ($obj=$this->db->fetch_object($resql)) + { + if ($obj->fk_product_type == 1) { $this->nb["services"]=$obj->nb; + } else { $this->nb["products"]=$obj->nb; + } + } $this->db->free($resql); - return 1; - } - else - { - dol_print_error($this->db); - $this->error=$this->db->error(); - return -1; - } - } + return 1; + } + else + { + dol_print_error($this->db); + $this->error=$this->db->error(); + return -1; + } + } /** * Return if object is a product * - * @return boolean True if it's a product + * @return boolean True if it's a product */ - function isProduct() - { - return ($this->type == Product::TYPE_PRODUCT ? true : false); - } + function isProduct() + { + return ($this->type == Product::TYPE_PRODUCT ? true : false); + } /** * Return if object is a product * - * @return boolean True if it's a service + * @return boolean True if it's a service */ - function isService() - { - return ($this->type == Product::TYPE_SERVICE ? true : false); - } + function isService() + { + return ($this->type == Product::TYPE_SERVICE ? true : false); + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps /** * Get a barcode from the module to generate barcode values. * Return value is stored into this->barcode * - * @param Product $object Object product or service - * @param string $type Barcode type (ean, isbn, ...) - * @return void + * @param Product $object Object product or service + * @param string $type Barcode type (ean, isbn, ...) + * @return void */ function get_barcode($object,$type='') { @@ -4520,18 +4533,18 @@ class Product extends CommonObject global $conf; $result=''; - if (! empty($conf->global->BARCODE_PRODUCT_ADDON_NUM)) - { - $dirsociete=array_merge(array('/core/modules/barcode/'),$conf->modules_parts['barcode']); + if (! empty($conf->global->BARCODE_PRODUCT_ADDON_NUM)) { + $dirsociete=array_merge(array('/core/modules/barcode/'), $conf->modules_parts['barcode']); foreach ($dirsociete as $dirroot) { $res=dol_include_once($dirroot.$conf->global->BARCODE_PRODUCT_ADDON_NUM.'.php'); - if ($res) break; + if ($res) { break; + } } $var = $conf->global->BARCODE_PRODUCT_ADDON_NUM; $mod = new $var; - $result=$mod->getNextValue($object,$type); + $result=$mod->getNextValue($object, $type); dol_syslog(get_class($this)."::get_barcode barcode=".$result." module=".$var); } @@ -4541,9 +4554,9 @@ class Product extends CommonObject /** * Initialise an instance with random values. * Used to build previews or test instances. - * id must be 0 if object instance is a specimen. + * id must be 0 if object instance is a specimen. * - * @return void + * @return void */ function initAsSpecimen() { @@ -4556,12 +4569,12 @@ class Product extends CommonObject $this->id=0; $this->ref = 'PRODUCT_SPEC'; $this->label = 'PRODUCT SPECIMEN'; - $this->description = 'This is description of this product specimen that was created the '.dol_print_date($now,'dayhourlog').'.'; + $this->description = 'This is description of this product specimen that was created the '.dol_print_date($now, 'dayhourlog').'.'; $this->specimen=1; $this->country_id=1; $this->tosell=1; $this->tobuy=1; - $this->tobatch=0; + $this->tobatch=0; $this->note='This is a comment (private)'; $this->date_creation = $now; $this->date_modification = $now; @@ -4581,283 +4594,277 @@ class Product extends CommonObject $this->volume = 300; $this->volume_unit = 0; - $this->barcode=-1; // Create barcode automatically + $this->barcode=-1; // Create barcode automatically } - /** - * Returns the text label from units dictionary - * - * @param string $type Label type (long or short) - * @return string|int <0 if ko, label if ok - */ - function getLabelOfUnit($type='long') - { - global $langs; + /** + * Returns the text label from units dictionary + * + * @param string $type Label type (long or short) + * @return string|int <0 if ko, label if ok + */ + function getLabelOfUnit($type='long') + { + global $langs; - if (!$this->fk_unit) { - return ''; - } + if (!$this->fk_unit) { + return ''; + } - $langs->load('products'); + $langs->load('products'); - $label_type = 'label'; + $label_type = 'label'; - if ($type == 'short') - { - $label_type = 'short_label'; - } + if ($type == 'short') { + $label_type = 'short_label'; + } - $sql = 'select '.$label_type.' from '.MAIN_DB_PREFIX.'c_units where rowid='.$this->fk_unit; - $resql = $this->db->query($sql); - if($resql && $this->db->num_rows($resql) > 0) - { - $res = $this->db->fetch_array($resql); - $label = $res[$label_type]; - $this->db->free($resql); - return $label; - } - else - { - $this->error=$this->db->error().' sql='.$sql; - dol_syslog(get_class($this)."::getLabelOfUnit Error ".$this->error, LOG_ERR); - return -1; - } - } + $sql = 'select '.$label_type.' from '.MAIN_DB_PREFIX.'c_units where rowid='.$this->fk_unit; + $resql = $this->db->query($sql); + if($resql && $this->db->num_rows($resql) > 0) { + $res = $this->db->fetch_array($resql); + $label = $res[$label_type]; + $this->db->free($resql); + return $label; + } + else + { + $this->error=$this->db->error().' sql='.$sql; + dol_syslog(get_class($this)."::getLabelOfUnit Error ".$this->error, LOG_ERR); + return -1; + } + } /** * Return if object has a sell-by date or eat-by date * - * @return boolean True if it's has + * @return boolean True if it's has */ - function hasbatch() - { - return ($this->status_batch == 1 ? true : false); - } + function hasbatch() + { + return ($this->status_batch == 1 ? true : false); + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps /** * Return minimum product recommended price * - * @return int Minimum recommanded price that is higher price among all suppliers * PRODUCT_MINIMUM_RECOMMENDED_PRICE + * @return int Minimum recommanded price that is higher price among all suppliers * PRODUCT_MINIMUM_RECOMMENDED_PRICE */ - function min_recommended_price() - { + function min_recommended_price() + { // phpcs:enable - global $conf; + global $conf; - $maxpricesupplier=0; + $maxpricesupplier=0; - if (! empty($conf->global->PRODUCT_MINIMUM_RECOMMENDED_PRICE)) - { - require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; - $product_fourn = new ProductFournisseur($this->db); - $product_fourn_list = $product_fourn->list_product_fournisseur_price($this->id, '', ''); + if (! empty($conf->global->PRODUCT_MINIMUM_RECOMMENDED_PRICE)) { + include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; + $product_fourn = new ProductFournisseur($this->db); + $product_fourn_list = $product_fourn->list_product_fournisseur_price($this->id, '', ''); - if (is_array($product_fourn_list) && count($product_fourn_list)>0) - { - foreach($product_fourn_list as $productfourn) - { - if ($productfourn->fourn_unitprice > $maxpricesupplier) - { - $maxpricesupplier = $productfourn->fourn_unitprice; - } - } + if (is_array($product_fourn_list) && count($product_fourn_list)>0) { + foreach($product_fourn_list as $productfourn) + { + if ($productfourn->fourn_unitprice > $maxpricesupplier) { + $maxpricesupplier = $productfourn->fourn_unitprice; + } + } - $maxpricesupplier *= $conf->global->PRODUCT_MINIMUM_RECOMMENDED_PRICE; - } - } + $maxpricesupplier *= $conf->global->PRODUCT_MINIMUM_RECOMMENDED_PRICE; + } + } - return $maxpricesupplier; - } + return $maxpricesupplier; + } - /** - * Sets object to supplied categories. - * - * Deletes object from existing categories not supplied. - * Adds it to non existing supplied categories. - * Existing categories are left untouch. - * - * @param int[]|int $categories Category or categories IDs + /** + * Sets object to supplied categories. + * + * Deletes object from existing categories not supplied. + * Adds it to non existing supplied categories. + * Existing categories are left untouch. + * + * @param int[]|int $categories Category or categories IDs * @return void - */ + */ public function setCategories($categories) { - // Handle single category - if (! is_array($categories)) { - $categories = array($categories); - } + // Handle single category + if (! is_array($categories)) { + $categories = array($categories); + } - // Get current categories - require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; - $c = new Categorie($this->db); - $existing = $c->containing($this->id, Categorie::TYPE_PRODUCT, 'id'); + // Get current categories + include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; + $c = new Categorie($this->db); + $existing = $c->containing($this->id, Categorie::TYPE_PRODUCT, 'id'); - // Diff - if (is_array($existing)) { - $to_del = array_diff($existing, $categories); - $to_add = array_diff($categories, $existing); - } else { - $to_del = array(); // Nothing to delete - $to_add = $categories; - } + // Diff + if (is_array($existing)) { + $to_del = array_diff($existing, $categories); + $to_add = array_diff($categories, $existing); + } else { + $to_del = array(); // Nothing to delete + $to_add = $categories; + } - // Process - foreach($to_del as $del) { - if ($c->fetch($del) > 0) { - $c->del_type($this, 'product'); - } - } - foreach ($to_add as $add) { - if ($c->fetch($add) > 0) { - $c->add_type($this, 'product'); - } - } + // Process + foreach($to_del as $del) { + if ($c->fetch($del) > 0) { + $c->del_type($this, 'product'); + } + } + foreach ($to_add as $add) { + if ($c->fetch($add) > 0) { + $c->add_type($this, 'product'); + } + } - return; - } + return; + } - /** - * Function used to replace a thirdparty id with another one. - * - * @param DoliDB $db Database handler - * @param int $origin_id Old thirdparty id - * @param int $dest_id New thirdparty id - * @return bool - */ - public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) - { - $tables = array( - 'product_customer_price', - 'product_customer_price_log' - ); + /** + * Function used to replace a thirdparty id with another one. + * + * @param DoliDB $db Database handler + * @param int $origin_id Old thirdparty id + * @param int $dest_id New thirdparty id + * @return bool + */ + public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id) + { + $tables = array( + 'product_customer_price', + 'product_customer_price_log' + ); - return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); - } + return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); + } - /** - * Generates prices for a product based on product multiprice generation rules - * - * @param User $user User that updates the prices - * @param float $baseprice Base price - * @param string $price_type Base price type - * @param float $price_vat VAT % tax - * @param int $npr NPR - * @param string $psq ¿? - * @return int -1 KO, 1 OK - */ - public function generateMultiprices(User $user, $baseprice, $price_type, $price_vat, $npr, $psq) - { - global $conf, $db; + /** + * Generates prices for a product based on product multiprice generation rules + * + * @param User $user User that updates the prices + * @param float $baseprice Base price + * @param string $price_type Base price type + * @param float $price_vat VAT % tax + * @param int $npr NPR + * @param string $psq ¿? + * @return int -1 KO, 1 OK + */ + public function generateMultiprices(User $user, $baseprice, $price_type, $price_vat, $npr, $psq) + { + global $conf, $db; - $sql = "SELECT rowid, level, fk_level, var_percent, var_min_percent FROM ".MAIN_DB_PREFIX."product_pricerules"; - $query = $db->query($sql); + $sql = "SELECT rowid, level, fk_level, var_percent, var_min_percent FROM ".MAIN_DB_PREFIX."product_pricerules"; + $query = $db->query($sql); - $rules = array(); + $rules = array(); - while ($result = $db->fetch_object($query)) { - $rules[$result->level] = $result; - } + while ($result = $db->fetch_object($query)) { + $rules[$result->level] = $result; + } - //Because prices can be based on other level's prices, we temporarily store them - $prices = array( - 1 => $baseprice - ); + //Because prices can be based on other level's prices, we temporarily store them + $prices = array( + 1 => $baseprice + ); - for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) { + for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) { - $price = $baseprice; - $price_min = $baseprice; + $price = $baseprice; + $price_min = $baseprice; - //We have to make sure it does exist and it is > 0 - //First price level only allows changing min_price - if ($i > 1 && isset($rules[$i]->var_percent) && $rules[$i]->var_percent) { - $price = $prices[$rules[$i]->fk_level] * (1 + ($rules[$i]->var_percent/100)); - } + //We have to make sure it does exist and it is > 0 + //First price level only allows changing min_price + if ($i > 1 && isset($rules[$i]->var_percent) && $rules[$i]->var_percent) { + $price = $prices[$rules[$i]->fk_level] * (1 + ($rules[$i]->var_percent/100)); + } - $prices[$i] = $price; + $prices[$i] = $price; - //We have to make sure it does exist and it is > 0 - if (isset($rules[$i]->var_min_percent) && $rules[$i]->var_min_percent) { - $price_min = $price * (1 - ($rules[$i]->var_min_percent/100)); - } + //We have to make sure it does exist and it is > 0 + if (isset($rules[$i]->var_min_percent) && $rules[$i]->var_min_percent) { + $price_min = $price * (1 - ($rules[$i]->var_min_percent/100)); + } - //Little check to make sure the price is modified before triggering generation - $check_amount = (($price == $this->multiprices[$i]) && ($price_min == $this->multiprices_min[$i])); - $check_type = ($baseprice == $this->multiprices_base_type[$i]); + //Little check to make sure the price is modified before triggering generation + $check_amount = (($price == $this->multiprices[$i]) && ($price_min == $this->multiprices_min[$i])); + $check_type = ($baseprice == $this->multiprices_base_type[$i]); - if ($check_amount && $check_type) { - continue; - } + if ($check_amount && $check_type) { + continue; + } - if ($this->updatePrice($price, $price_type, $user, $price_vat, $price_min, $i, $npr, $psq, true) < 0) { - return -1; - } - } + if ($this->updatePrice($price, $price_type, $user, $price_vat, $price_min, $i, $npr, $psq, true) < 0) { + return -1; + } + } - return 1; - } + return 1; + } - /** - * Returns the rights used for this class - * @return stdClass - */ - public function getRights() - { - global $user; + /** + * Returns the rights used for this class + * + * @return stdClass + */ + public function getRights() + { + global $user; - if ($this->isProduct()) { - return $user->rights->produit; - } else { - return $user->rights->service; - } - } + if ($this->isProduct()) { + return $user->rights->produit; + } else { + return $user->rights->service; + } + } - /** - * Load information for tab info - * - * @param int $id Id of thirdparty to load - * @return void - */ - function info($id) - { - $sql = "SELECT p.rowid, p.ref, p.datec as date_creation, p.tms as date_modification,"; - $sql.= " p.fk_user_author, p.fk_user_modif"; - $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element." as p"; - $sql.= " WHERE p.rowid = ".$id; + /** + * Load information for tab info + * + * @param int $id Id of thirdparty to load + * @return void + */ + function info($id) + { + $sql = "SELECT p.rowid, p.ref, p.datec as date_creation, p.tms as date_modification,"; + $sql.= " p.fk_user_author, p.fk_user_modif"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element." as p"; + $sql.= " WHERE p.rowid = ".$id; - $result=$this->db->query($sql); - if ($result) - { - if ($this->db->num_rows($result)) - { - $obj = $this->db->fetch_object($result); + $result=$this->db->query($sql); + if ($result) { + if ($this->db->num_rows($result)) { + $obj = $this->db->fetch_object($result); - $this->id = $obj->rowid; + $this->id = $obj->rowid; - if ($obj->fk_user_author) { - $cuser = new User($this->db); - $cuser->fetch($obj->fk_user_author); - $this->user_creation = $cuser; - } + if ($obj->fk_user_author) { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } - if ($obj->fk_user_modif) { - $muser = new User($this->db); - $muser->fetch($obj->fk_user_modif); - $this->user_modification = $muser; - } + if ($obj->fk_user_modif) { + $muser = new User($this->db); + $muser->fetch($obj->fk_user_modif); + $this->user_modification = $muser; + } - $this->ref = $obj->ref; - $this->date_creation = $this->db->jdate($obj->date_creation); - $this->date_modification = $this->db->jdate($obj->date_modification); - } + $this->ref = $obj->ref; + $this->date_creation = $this->db->jdate($obj->date_creation); + $this->date_modification = $this->db->jdate($obj->date_modification); + } - $this->db->free($result); - } - else - { - dol_print_error($this->db); - } - } + $this->db->free($result); + } + else + { + dol_print_error($this->db); + } + } } diff --git a/htdocs/product/fournisseurs.php b/htdocs/product/fournisseurs.php index 26346b1017b..08459729e87 100644 --- a/htdocs/product/fournisseurs.php +++ b/htdocs/product/fournisseurs.php @@ -751,16 +751,17 @@ SCRIPT; $num = count($product_fourn_list); if (($num + ($offset * $limit)) < $nbtotalofrecords) $num++; - print_barre_liste($langs->trans('SupplierPrices'), $page, $_SERVEUR ['PHP_SELF'], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_accountancy.png', 0, '', '', $limit, 1); + print_barre_liste($langs->trans('SupplierPrices'), $page, $_SERVEUR ['PHP_SELF'], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_accountancy.png', 0, '', '', $limit, 1); // Suppliers list title - print '
'; - print ''; + print '
'; + print '
'; if ($object->isProduct()) $nblignefour=4; else $nblignefour=4; $param="&id=".$object->id; print ''; + print_liste_field_titre("AppliedPricesFrom",$_SERVER["PHP_SELF"],"pfp.datec","",$param,"",$sortfield,$sortorder); print_liste_field_titre("Suppliers",$_SERVER["PHP_SELF"],"s.nom","",$param,"",$sortfield,$sortorder); print_liste_field_titre("SupplierRef",$_SERVER["PHP_SELF"],"","",$param,"",$sortfield,$sortorder); if (!empty($conf->global->FOURN_PRODUCT_AVAILABILITY)) print_liste_field_titre("Availability",$_SERVER["PHP_SELF"],"pfp.fk_availability","",$param,"",$sortfield,$sortorder); @@ -788,10 +789,13 @@ SCRIPT; { print ''; + // Date from + print ''; + // Supplier print ''; - // Supplier + // Supplier ref print ''; // Availability diff --git a/htdocs/product/inventory/card.php b/htdocs/product/inventory/card.php index 2784e3c303b..24848ad26c5 100644 --- a/htdocs/product/inventory/card.php +++ b/htdocs/product/inventory/card.php @@ -53,7 +53,7 @@ $diroutputmassaction=$conf->stock->dir_output . '/temp/massgeneration/'.$user->i $hookmanager->initHooks(array('inventorycard')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('inventory'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost('inventory','','search_'); // Initialize array of search criterias $search_all=trim(GETPOST("search_all",'alpha')); diff --git a/htdocs/product/inventory/list.php b/htdocs/product/inventory/list.php index a5592c87f5d..95bb82aea50 100644 --- a/htdocs/product/inventory/list.php +++ b/htdocs/product/inventory/list.php @@ -59,7 +59,7 @@ $diroutputmassaction=$conf->inventory->dir_output . '/temp/massgeneration/'.$use $hookmanager->initHooks(array('inventorylist')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('inventory'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Default sort order (if not yet defined by previous GETPOST) if (! $sortfield) $sortfield="t.".key($object->fields); // Set here default search field. By default 1st field in definition. diff --git a/htdocs/product/list.php b/htdocs/product/list.php index a20308c72a9..03d69a7a243 100644 --- a/htdocs/product/list.php +++ b/htdocs/product/list.php @@ -99,7 +99,7 @@ $form=new Form($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('product'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); if (empty($action)) $action='list'; diff --git a/htdocs/product/stock/movement_list.php b/htdocs/product/stock/movement_list.php index 547bf09441d..bd5ac4a4345 100644 --- a/htdocs/product/stock/movement_list.php +++ b/htdocs/product/stock/movement_list.php @@ -90,7 +90,7 @@ $formfile = new FormFile($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('movement'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); $arrayfields=array( 'm.rowid'=>array('label'=>$langs->trans("Ref"), 'checked'=>1), diff --git a/htdocs/product/stock/productlot_card.php b/htdocs/product/stock/productlot_card.php index f72a28dbc90..dff2aa8e0e0 100644 --- a/htdocs/product/stock/productlot_card.php +++ b/htdocs/product/stock/productlot_card.php @@ -1,7 +1,6 @@ +/* Copyright (C) 2007-2018 Laurent Destailleur * Copyright (C) 2018 All-3kcis - * Copyright (C) ---Put here your own copyright and developer email--- * * 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 @@ -317,7 +316,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea } - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; $shownav = 1; if ($user->societe_id && ! in_array('batch', explode(',',$conf->global->MAIN_MODULES_FOR_EXTERNAL))) $shownav=0; diff --git a/htdocs/product/stock/productlot_document.php b/htdocs/product/stock/productlot_document.php index 29b01e376b1..c5c1e7c37f0 100644 --- a/htdocs/product/stock/productlot_document.php +++ b/htdocs/product/stock/productlot_document.php @@ -131,7 +131,7 @@ if ($object->id) } - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; $shownav = 1; if ($user->societe_id && ! in_array('batch', explode(',',$conf->global->MAIN_MODULES_FOR_EXTERNAL))) $shownav=0; diff --git a/htdocs/product/stock/productlot_list.php b/htdocs/product/stock/productlot_list.php index 4d9916bfa9c..6de8be264a9 100644 --- a/htdocs/product/stock/productlot_list.php +++ b/htdocs/product/stock/productlot_list.php @@ -79,7 +79,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('product_lot'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/projet/activity/perday.php b/htdocs/projet/activity/perday.php index 0ebfa37bef8..e291bf84f67 100644 --- a/htdocs/projet/activity/perday.php +++ b/htdocs/projet/activity/perday.php @@ -416,7 +416,7 @@ print '
'; $titleassigntask = $langs->transnoentities("AssignTaskToMe"); if ($usertoprocess->id != $user->id) $titleassigntask = $langs->transnoentities("AssignTaskToUser", $usertoprocess->getFullName($langs)); print '
'; -$formproject->selectTasks($socid?$socid:-1, $taskid, 'taskid', 32, 0, 1, 1); +$formproject->selectTasks($socid?$socid:-1, $taskid, 'taskid', 32, 0, 1, 1, 0, 0, '', '', 'all', $usertoprocess); print '
'; print ' '; print $formcompany->selectTypeContact($object, '', 'type','internal','rowid', 0, 'maxwidth150onsmartphone'); diff --git a/htdocs/projet/activity/perweek.php b/htdocs/projet/activity/perweek.php index f68c899992b..cbf72d05788 100644 --- a/htdocs/projet/activity/perweek.php +++ b/htdocs/projet/activity/perweek.php @@ -418,7 +418,7 @@ print '
'; $titleassigntask = $langs->transnoentities("AssignTaskToMe"); if ($usertoprocess->id != $user->id) $titleassigntask = $langs->transnoentities("AssignTaskToUser", $usertoprocess->getFullName($langs)); print '
'; -$formproject->selectTasks($socid?$socid:-1, $taskid, 'taskid', 32, 0, 1, 1); +$formproject->selectTasks($socid?$socid:-1, $taskid, 'taskid', 32, 0, 1, 1, 0, 0, '', '', 'all', $usertoprocess); print '
'; print ' '; print $formcompany->selectTypeContact($object, '', 'type','internal','rowid', 0, 'maxwidth150onsmartphone'); diff --git a/htdocs/projet/list.php b/htdocs/projet/list.php index 2c8fb856938..6f3ca7215a7 100644 --- a/htdocs/projet/list.php +++ b/htdocs/projet/list.php @@ -105,7 +105,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('projet'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( @@ -600,7 +600,7 @@ if (! empty($arrayfields['p.public']['checked'])) if (! empty($arrayfields['p.fk_opp_status']['checked'])) { print '
'; } if (! empty($arrayfields['p.opp_amount']['checked'])) @@ -809,7 +809,7 @@ while ($i < min($num,$limit)) if (! empty($arrayfields['p.fk_opp_status']['checked'])) { print ''; if (! $i) $totalarray['nbfield']++; } diff --git a/htdocs/projet/tasks.php b/htdocs/projet/tasks.php index e42c71cc0f6..093d66dd629 100644 --- a/htdocs/projet/tasks.php +++ b/htdocs/projet/tasks.php @@ -477,7 +477,7 @@ if ($action == 'create' && $user->rights->projet->creer && (empty($object->third print ''; - print ''; + print ''; // Type print ''; diff --git a/htdocs/resource/list.php b/htdocs/resource/list.php index 4492d74be65..55dddcc0a48 100644 --- a/htdocs/resource/list.php +++ b/htdocs/resource/list.php @@ -50,7 +50,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels=$extrafields->fetch_name_optionals_label($object->table_element); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); $search_ref=GETPOST("search_ref"); $search_type=GETPOST("search_type"); diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index d267dba7732..9fe4ff4d4f3 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -417,8 +417,8 @@ if (empty($reshook)) $object->idprof5 = trim(GETPOST('idprof5', 'alpha')); $object->idprof6 = trim(GETPOST('idprof6', 'alpha')); $object->prefix_comm = GETPOST('prefix_comm', 'alpha'); - $object->code_client = GETPOST('customer_code', 'alpha'); - $object->code_fournisseur = GETPOST('supplier_code', 'alpha'); + $object->code_client = GETPOSTISSET('customer_code')?GETPOST('customer_code', 'alpha'):GETPOST('code_client', 'alpha'); + $object->code_fournisseur = GETPOSTISSET('supplier_code')?GETPOST('supplier_code', 'alpha'):GETPOST('code_fournisseur', 'alpha'); $object->capital = GETPOST('capital', 'alpha'); $object->barcode = GETPOST('barcode', 'alpha'); diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index 59e79376411..2411623e4aa 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -13,6 +13,7 @@ * Copyright (C) 2014-2015 Marcos García * Copyright (C) 2015 Raphaël Doursenaud * Copyright (C) 2017 Rui Strecht + * Copyright (C) 2018 Philippe Grand * * 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 @@ -947,6 +948,24 @@ class Societe extends CommonObject // We don't check when update called during a create because verify was already done. // For a merge, we suppose source data is clean and a customer code of a deleted thirdparty must be accepted into a target thirdparty with empty code without duplicate error $result = $this->verify(); + + // If there is only one error and error is ErrorBadCustomerCodeSyntax and we don't change customer code, we allow the update + // So we can update record that were using and old numbering rule. + if (is_array($this->errors)) + { + if (in_array('ErrorBadCustomerCodeSyntax', $this->errors) && is_object($this->oldcopy) && $this->oldcopy->code_client == $this->code_client) + { + if (($key = array_search('ErrorBadCustomerCodeSyntax', $this->errors)) !== false) unset($this->errors[$key]); // Remove error message + } + if (in_array('ErrorBadSupplierCodeSyntax', $this->errors) && is_object($this->oldcopy) && $this->oldcopy->code_fournisseur == $this->code_fournisseur) + { + if (($key = array_search('ErrorBadSupplierCodeSyntax', $this->errors)) !== false) unset($this->errors[$key]); // Remove error message + } + if (empty($this->errors)) // If there is no more error, we can make like if there is no error at all + { + $result = 0; + } + } } if ($result >= 0) @@ -1509,6 +1528,19 @@ class Societe extends CommonObject } } + // Remove links to subsidiaries companies + if (! $error) + { + $sql = "UPDATE ".MAIN_DB_PREFIX."societe"; + $sql.= " SET parent = NULL"; + $sql.= " WHERE parent = " . $id; + if (! $this->db->query($sql)) + { + $error++; + $this->errors[] = $this->db->lasterror(); + } + } + // Remove third party if (! $error) { @@ -3827,6 +3859,7 @@ class Societe extends CommonObject * * @return int Amount in debt for thirdparty * @deprecated + * @see getOutstandingBills() */ function get_OutstandingBill() { diff --git a/htdocs/societe/list.php b/htdocs/societe/list.php index 30c3c06675b..7ddc4ac575e 100644 --- a/htdocs/societe/list.php +++ b/htdocs/societe/list.php @@ -121,7 +121,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('societe'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/societe/website.php b/htdocs/societe/website.php index 3f6f3ba197a..032ece6f4c4 100644 --- a/htdocs/societe/website.php +++ b/htdocs/societe/website.php @@ -63,7 +63,7 @@ $diroutputmassaction=$conf->website->dir_output . '/temp/massgeneration/'.$user- $hookmanager->initHooks(array('websitethirdpartylist')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('thirdpartyaccount'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost('thirdpartyaccount','','search_'); unset($objectwebsiteaccount->fields['fk_soc']); // Remove this field, we are already on the thirdparty diff --git a/htdocs/stripe/payment.php b/htdocs/stripe/payment.php index 8bada8a96e7..7a06da294ab 100644 --- a/htdocs/stripe/payment.php +++ b/htdocs/stripe/payment.php @@ -8,7 +8,7 @@ * Copyright (C) 2014 Raphaël Doursenaud * Copyright (C) 2014 Teddy Andreotti <125155@supinfo.com> * Copyright (C) 2015 Juanjo Menent - * Copyright (C) 2018 ThibaultFOUCART + * Copyright (C) 2018 Thibault FOUCART * Copyright (C) 2018 Frédéric France * * This program is free software; you can redistribute it and/or modify @@ -63,21 +63,6 @@ $addwarning=0; $multicurrency_amounts=array(); $multicurrency_amountsresttopay=array(); -if (! empty($conf->stripe->enabled)) -{ - $service = 'StripeTest'; - $servicestatus = 0; - if (! empty($conf->global->STRIPE_LIVE) && ! GETPOST('forcesandbox','alpha')) - { - $service = 'StripeLive'; - $servicestatus = 0; - } - - $stripe = new Stripe($db); - $stripeacc = $stripe->getStripeAccount($service); // Stripe OAuth connect account of dolibarr user (no network access here) - $stripecu = $stripe->getStripeCustomerAccount($object->id, $servicestatus); // Get thirdparty cu_... -} - // Security check $socid=0; if ($user->societe_id > 0) @@ -93,6 +78,20 @@ if ($facid > 0) $ret=$object->fetch($facid); } +if (! empty($conf->stripe->enabled)) +{ + $service = 'StripeTest'; + $servicestatus = 0; + if (! empty($conf->global->STRIPE_LIVE) && ! GETPOST('forcesandbox','alpha')) + { + $service = 'StripeLive'; + $servicestatus = 0; + } + + $stripe=new Stripe($db); + $stripeacc = $stripe->getStripeAccount($service); // Stripe OAuth connect account of dolibarr user (no network access here) +} + // Initialize technical object to manage hooks of paiements. Note that conf->hooks_modules contains array array $hookmanager->initHooks(array('paiementcard','globalcard')); @@ -308,11 +307,15 @@ if (empty($reshook)) } } + + $societe = new Societe($db); $societe->fetch($facture->socid); dol_syslog("Create charge", LOG_DEBUG, 0, '_stripe'); - $charge=$stripe->CreatePaymentStripe($stripeamount,"EUR","invoice",$facid,$source,$customer->id,$stripe->getStripeAccount($conf->entity)); + $stripecu = $stripe->getStripeCustomerAccount($societe->id, $servicestatus); // Get thirdparty cu_... + + $charge=$stripe->createPaymentStripe($stripeamount,$facture->multicurrency_code,"invoice",$facid,$source,$stripecu,$stripeacc,$servicestatus); if (!$error) { @@ -324,6 +327,8 @@ if (empty($reshook)) $paiement->paiementid = dol_getIdFromCode($db,$paiementcode,'c_paiement'); $paiement->num_paiement = $charge->message; $paiement->note = GETPOST('comment'); + $paiement->ext_payment_id = $charge->id; + $paiement->ext_payment_site = $service; } if (! $error) @@ -389,7 +394,7 @@ if (empty($reshook)) } else { - $loc = DOL_URL_ROOT.'/stripeconnect/payment.php?facid='.$facid.'&action=create&error='.$e->getMessage(); + $loc = DOL_URL_ROOT.'/stripe/payment.php?facid='.$facid.'&action=create&error='.$charge->message; $db->rollback(); header('Location: '.$loc); diff --git a/htdocs/supplier_proposal/list.php b/htdocs/supplier_proposal/list.php index 9dea9c7f8dd..642d4610a10 100644 --- a/htdocs/supplier_proposal/list.php +++ b/htdocs/supplier_proposal/list.php @@ -117,7 +117,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('supplier_proposal'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // List of fields to search into when doing a "search in all" diff --git a/htdocs/takepos/README.md b/htdocs/takepos/README.md index 40d4742af27..bdbe6bbd2e8 100644 --- a/htdocs/takepos/README.md +++ b/htdocs/takepos/README.md @@ -1,100 +1,9 @@ -# TAKEPOS FOR DOLIBARR ERP CRM +# TAKEPOS ## Features -Touch Screen POS +Add a Touch Screen POS (Point Of Sale) to your ERP. -Other modules are available on Dolistore.com. - - - -### Translations - -Translations can be define manually by editing files into directories [langs](langs). - - - - - - - -Licenses --------- - -### Main code - -![GPLv3 logo](img/gplv3.png) - -GPLv3 or (at your option) any later version. - -See [COPYING](COPYING) for more information. - -#### Documentation - -All texts and readmes. - -![GFDL logo](img/gfdl.png) diff --git a/htdocs/takepos/customers.php b/htdocs/takepos/customers.php index 99b6d1eaf89..5a67998f180 100644 --- a/htdocs/takepos/customers.php +++ b/htdocs/takepos/customers.php @@ -144,7 +144,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('societe'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost('societe','','search_'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( diff --git a/htdocs/takepos/pay.php b/htdocs/takepos/pay.php index c0bfc35294a..898b9069ba0 100644 --- a/htdocs/takepos/pay.php +++ b/htdocs/takepos/pay.php @@ -41,7 +41,7 @@ $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."facture where facnumber='(PROV-POS-".$ $resql = $db->query($sql); $row = $db->fetch_array ($resql); $placeid=$row[0]; -if (! $placeid) $placeid=0; // Developing error message with no lines +if (! $placeid) $placeid=0; // Invoice not exist else{ $invoice = new Facture($db); $invoice->fetch($placeid); @@ -101,15 +101,15 @@ $langs->loadLangs(array("main", "bills", "cashdesk")); - + - + - + diff --git a/htdocs/theme/eldy/style.css.php b/htdocs/theme/eldy/style.css.php index 4d80f57b342..487d4125284 100644 --- a/htdocs/theme/eldy/style.css.php +++ b/htdocs/theme/eldy/style.css.php @@ -331,7 +331,7 @@ input, select { } /* Focus definitions must be after standard definition */ -textarea:focus, button:focus { +textarea:focus { /* v6 box-shadow: 0 0 4px #8091BF; */ border: 1px solid #aaa !important; } @@ -907,7 +907,8 @@ div.fiche { min-width: 150px; } .thumbstat150 { - min-width: 170px; + /* min-width: 170px; */ + width: 170px; } .thumbstat, .thumbstat150 { browser->name == 'ie') { ?> @@ -1073,6 +1074,9 @@ select.selectarrowonleft option { /* border-bottom: 1px solid #BBB; */ /* max-width: inherit; why this ? */ } + input[type=text], input[type=password] { + max-width: 180px; + } .hideonsmartphone { display: none; } .hideonsmartphoneimp { display: none !important; } @@ -3312,6 +3316,7 @@ ul.noborder li:nth-child(even):not(.liste_titre) { .thumbstat150 { flex: 1 1 110px; margin-bottom: 8px; + width: 160px; } .dashboardlineindicator { float: left; @@ -3566,7 +3571,7 @@ div.boximport { .widthpictotitle { width: 40px; text-align: ; } .dolgraphtitle { margin-top: 6px; margin-bottom: 4px; } -.dolgraphtitlecssboxes { margin: 0px; } +.dolgraphtitlecssboxes { /* margin: 0px; */ } .legendColorBox, .legendLabel { border: none !important; } div.dolgraph div.legend, div.dolgraph div.legend div { background-color: rgba(255,255,255,0) !important; } div.dolgraph div.legend table tbody tr { height: auto; } diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index e6842d97fe3..72edb17b719 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -3249,7 +3249,7 @@ div .tdtop { margin: 3px; border: 1px solid #ddd; box-shadow: none; - background: #ddd; + background: #eee; } .thumbstat { flex: 1 1 110px; @@ -3486,7 +3486,7 @@ div.boximport { .widthpictotitle { width: 40px; text-align: ; } .dolgraphtitle { margin-top: 6px; margin-bottom: 4px; } -.dolgraphtitlecssboxes { margin: 0px; } +.dolgraphtitlecssboxes { /* margin: 0px; */ } .legendColorBox, .legendLabel { border: none !important; } div.dolgraph div.legend, div.dolgraph div.legend div { background-color: rgba(255,255,255,0) !important; } div.dolgraph div.legend table tbody tr { height: auto; } diff --git a/htdocs/ticket/card.php b/htdocs/ticket/card.php index c4718c395b3..ab16161e844 100644 --- a/htdocs/ticket/card.php +++ b/htdocs/ticket/card.php @@ -49,7 +49,7 @@ $id = GETPOST('id', 'int'); $track_id = GETPOST('track_id', 'alpha', 3); $ref = GETPOST('ref', 'alpha'); $projectid = GETPOST('projectid', 'int'); -$action = GETPOST('action', 'alpha', 3); +$action = GETPOST('action', 'aZ09'); // Initialize technical object to manage hooks of ticket. Note that conf->hooks_modules contains array array $hookmanager->initHooks(array('ticketcard','globalcard')); @@ -58,7 +58,7 @@ $object = new Ticket($db); $extrafields = new ExtraFields($db); // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Initialize array of search criterias $search_all=trim(GETPOST("search_all",'alpha')); diff --git a/htdocs/ticket/class/actions_ticket.class.php b/htdocs/ticket/class/actions_ticket.class.php index 7e8ae39564a..bf1e73404f1 100644 --- a/htdocs/ticket/class/actions_ticket.class.php +++ b/htdocs/ticket/class/actions_ticket.class.php @@ -258,7 +258,7 @@ class ActionsTicket // Extrafields $extrafields = new ExtraFields($this->db); $extralabels = $extrafields->fetch_name_optionals_label($fichinter->table_element); - $array_options = $extrafields->getOptionalsFromPost($extralabels); + $array_options = $extrafields->getOptionalsFromPost($fichinter->table_element); $fichinter->array_options = $array_options; $id = $fichinter->create($user); @@ -827,6 +827,7 @@ class ActionsTicket global $mysoc, $conf, $langs; + $object = new Ticket($this->db); $error = 0; $ret = $object->fetch('', '', GETPOST('track_id','alpha')); $object->socid = $object->fk_soc; @@ -1203,15 +1204,14 @@ class ActionsTicket public function viewTicketMessages($show_private, $show_user = true) { global $conf, $langs, $user; - global $object; // Load logs in cache - $ret = $object->loadCacheMsgsTicket(); + $ret = $this->dao->loadCacheMsgsTicket(); $action = GETPOST('action'); $this->viewTicketOriginalMessage($user, $action); - if (is_array($object->cache_msgs_ticket) && count($object->cache_msgs_ticket) > 0) { + if (is_array($this->dao->cache_msgs_ticket) && count($this->dao->cache_msgs_ticket) > 0) { print_titre($langs->trans('TicketMailExchanges')); print '
'.dol_print_date($productfourn->date_creation, 'dayhour').''.$productfourn->getSocNomUrl(1,'supplier').''.$productfourn->fourn_ref.''; - print $formproject->selectOpportunityStatus('search_opp_status', $search_opp_status, 1, 1, 1, 0, 'maxwidth100'); + print $formproject->selectOpportunityStatus('search_opp_status', $search_opp_status, 1, 0, 1, 0, 'maxwidth100'); print ''; - if ($obj->opp_status_code) print $langs->trans("OppStatusShort".$obj->opp_status_code); + if ($obj->opp_status_code) print $langs->trans("OppStatus".$obj->opp_status_code); print '
'.$langs->trans("AffectedTo").''; $contactsofproject=(! empty($object->id)?$object->getListContactId('internal'):''); - if (count($contactsofproject)) + if (is_array($contactsofproject) && count($contactsofproject)) { print $form->select_dolusers($user->id, 'userid', 0, '', 0, '', $contactsofproject, 0, 0, 0, '', 0, '', 'maxwidth300'); } diff --git a/htdocs/projet/tasks/list.php b/htdocs/projet/tasks/list.php index ea6236d3e18..93ff5fab7a8 100644 --- a/htdocs/projet/tasks/list.php +++ b/htdocs/projet/tasks/list.php @@ -79,7 +79,7 @@ $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('projet_task'); -$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); +$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_'); // Security check $socid=0; diff --git a/htdocs/public/payment/paymentok.php b/htdocs/public/payment/paymentok.php index a1fd65940d9..3ad71487dfc 100644 --- a/htdocs/public/payment/paymentok.php +++ b/htdocs/public/payment/paymentok.php @@ -631,7 +631,7 @@ if ($ispaymentok) $paiement->num_paiement = ''; $paiement->note_public = 'Online payment '.dol_print_date($now, 'standard').' from '.$ipaddress; $paiement->ext_payment_id = $TRANSACTIONID; - $paiement->ext_payment_site = $paymentmethod; + $paiement->ext_payment_site = $service; if (! $error) { diff --git a/htdocs/public/stripe/ipn.php b/htdocs/public/stripe/ipn.php index 45fd6489f92..23c073e5c0c 100644 --- a/htdocs/public/stripe/ipn.php +++ b/htdocs/public/stripe/ipn.php @@ -23,8 +23,6 @@ $entity=(! empty($_GET['entity']) ? (int) $_GET['entity'] : (! empty($_POST['ent if (is_numeric($entity)) define("DOLENTITY", $entity); require '../../main.inc.php'; - -if (empty($conf->stripe->enabled)) accessforbidden('',0,0,1); require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; require_once DOL_DOCUMENT_ROOT.'/includes/stripe/init.php'; @@ -37,19 +35,22 @@ require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; require_once DOL_DOCUMENT_ROOT .'/core/class/CMailFile.class.php'; +if (empty($conf->stripe->enabled)) accessforbidden('',0,0,1); + // You can find your endpoint's secret in your webhook settings -if (isset($_GET['connect'])){ +if (isset($_GET['connect'])) +{ if (isset($_GET['test'])) { $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_CONNECT_KEY; $service = 'StripeTest'; - $servicestatus = 0; + $servicestatus = 0; } else { $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_CONNECT_KEY; $service = 'StripeLive'; - $servicestatus = 1; + $servicestatus = 1; } } else { @@ -57,15 +58,16 @@ else { { $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_KEY; $service = 'StripeTest'; - $servicestatus = 0; + $servicestatus = 0; } else { $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_KEY; $service = 'StripeLive'; - $servicestatus = 1; + $servicestatus = 1; } } + $payload = @file_get_contents("php://input"); $sig_header = $_SERVER["HTTP_STRIPE_SIGNATURE"]; $event = null; @@ -127,7 +129,7 @@ $stripe=new Stripe($db); if ($event->type == 'payout.created') { $error=0; - $result=dolibarr_set_const($db, $servicestatus."_NEXTPAYOUT", date('Y-m-d H:i:s',$event->data->object->arrival_date), 'chaine', 0, '', $conf->entity); + $result=dolibarr_set_const($db, $service."_NEXTPAYOUT", date('Y-m-d H:i:s',$event->data->object->arrival_date), 'chaine', 0, '', $conf->entity); if ($result > 0) { @@ -172,7 +174,7 @@ if ($event->type == 'payout.created') { elseif ($event->type == 'payout.paid') { global $conf; $error=0; - $result=dolibarr_set_const($db, $servicestatus."_NEXTPAYOUT",null,'chaine',0,'',$conf->entity); + $result=dolibarr_set_const($db, $service."_NEXTPAYOUT",null,'chaine',0,'',$conf->entity); if ($result) { $langs->load("errors"); @@ -187,16 +189,10 @@ elseif ($event->type == 'payout.paid') { $accountfrom->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS); $accountto=new Account($db); - $accountto->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_BANKTRANFERS); + $accountto->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_BANKTRANSFERS); - if ($accountto->currency_code != $accountfrom->currency_code) { - $error++; - setEventMessages($langs->trans("ErrorTransferBetweenDifferentCurrencyNotPossible"), null, 'errors'); - } - - if ($accountto->id != $accountfrom->id) + if (($accountto->id != $accountfrom->id) && empty($error)) { - $bank_line_id_from=0; $bank_line_id_to=0; $result=0; @@ -207,8 +203,7 @@ elseif ($event->type == 'payout.paid') { if (! $error) $bank_line_id_from = $accountfrom->addline($dateo, $typefrom, $label, -1*price2num($amount), '', '', $user); if (! ($bank_line_id_from > 0)) $error++; - if ((! $error) && ($accountto->currency_code == $accountfrom->currency_code)) $bank_line_id_to = $accountto->addline($dateo, $typeto, $label, price2num($amount), '', '', $user); - if ((! $error) && ($accountto->currency_code != $accountfrom->currency_code)) $bank_line_id_to = $accountto->addline($dateo, $typeto, $label, price2num($amount_to), '', '', $user); + if (! $error) $bank_line_id_to = $accountto->addline($dateo, $typeto, $label, price2num($amount), '', '', $user); if (! ($bank_line_id_to > 0)) $error++; if (! $error) $result=$accountfrom->add_url_line($bank_line_id_from, $bank_line_id_to, DOL_URL_ROOT.'/compta/bank/ligne.php?rowid=', '(banktransfert)', 'banktransfert'); @@ -217,11 +212,35 @@ elseif ($event->type == 'payout.paid') { if (! ($result > 0)) $error++; } - // TODO Use CMail and translation - $body = "Un virement de ".price2num($event->data->object->amount/100)." ".$event->data->object->currency." a ete effectue sur votre compte le ".date('d-m-Y H:i:s',$event->data->object->arrival_date); - $subject = '[NOTIFICATION] Virement effectué'; - $headers = 'From: "'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; - mail(''.$conf->global->MAIN_INFO_SOCIETE_MAIL.'', $subject, $body, $headers); + $subject = '[NOTIFICATION] Stripe payout done'; + if (!empty($user->email)) { + $sendto = dolGetFirstLastname($user->firstname, $user->lastname) . " <".$user->email.">"; + } else { + $sendto = $conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; + } + $replyto = $sendto; + $sendtocc = ''; + if (!empty($conf->global->ONLINE_PAYMENT_SENDEMAIL)) { + $sendtocc = $conf->global->ONLINE_PAYMENT_SENDEMAIL.'" <'.$conf->global->ONLINE_PAYMENT_SENDEMAIL.'>'; + } + + $message = "A bank transfer of ".price2num($event->data->object->amount/100)." ".$event->data->object->currency." has been done to your account the ".dol_print_date($event->data->object->arrival_date, 'dayhour'); + + $mailfile = new CMailFile( + $subject, + $sendto, + $replyto, + $message, + array(), + array(), + array(), + $sendtocc, + '', + 0, + -1 + ); + + $ret = $mailfile->sendfile(); return 1; } diff --git a/htdocs/public/stripe/newpayment.php b/htdocs/public/stripe/newpayment.php index fcc7acd29ad..cb22aa72cd4 100644 --- a/htdocs/public/stripe/newpayment.php +++ b/htdocs/public/stripe/newpayment.php @@ -36,7 +36,6 @@ $entity=(! empty($_GET['entity']) ? (int) $_GET['entity'] : (! empty($_POST['ent if (is_numeric($entity)) define("DOLENTITY", $entity); require '../../main.inc.php'; - require_once DOL_DOCUMENT_ROOT.'/stripe/config.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; diff --git a/htdocs/public/ticket/list.php b/htdocs/public/ticket/list.php index 5ab17fedba2..f11583ac64d 100644 --- a/htdocs/public/ticket/list.php +++ b/htdocs/public/ticket/list.php @@ -44,7 +44,7 @@ $langs->loadLangs(array("companies","other","ticket")); // Get parameters $track_id = GETPOST('track_id', 'alpha'); -$action = GETPOST('action', 'alpha', 3); +$action = GETPOST('action', 'aZ09'); $email = GETPOST('email', 'alpha'); if (GETPOST('btn_view_ticket_list')) { @@ -182,7 +182,7 @@ if ($action == "view_ticketlist") // fetch optionals attributes and labels $extrafields = new ExtraFields($db); $extralabels = $extrafields->fetch_name_optionals_label('ticket'); - $search_array_options = $extrafields->getOptionalsFromPost($extralabels, '', 'search_'); + $search_array_options = $extrafields->getOptionalsFromPost('ticket', '', 'search_'); $filter = array(); $param = ''; diff --git a/htdocs/public/ticket/view.php b/htdocs/public/ticket/view.php index b5288b97a25..af4585622dd 100644 --- a/htdocs/public/ticket/view.php +++ b/htdocs/public/ticket/view.php @@ -45,7 +45,7 @@ $langs->loadLangs(array("companies","other","ticket")); // Get parameters $track_id = GETPOST('track_id', 'alpha'); -$action = GETPOST('action', 'alpha', 3); +$action = GETPOST('action', 'aZ09'); $email = GETPOST('email', 'alpha'); if (GETPOST('btn_view_ticket')) { diff --git a/htdocs/resource/card.php b/htdocs/resource/card.php index 49cad87e990..8fd7f5bcd13 100644 --- a/htdocs/resource/card.php +++ b/htdocs/resource/card.php @@ -250,7 +250,7 @@ if ($action == 'create' || $object->fetch($id) > 0) // Ref print '
'.$langs->trans("ResourceFormLabel_ref").'
'.$langs->trans("ResourceType").'
'; @@ -1228,7 +1228,7 @@ class ActionsTicket print ''; } - foreach ($object->cache_msgs_ticket as $id => $arraymsgs) { + foreach ($this->dao->cache_msgs_ticket as $id => $arraymsgs) { if (!$arraymsgs['private'] || ($arraymsgs['private'] == "1" && $show_private) ) { diff --git a/htdocs/ticket/history.php b/htdocs/ticket/history.php index c840f3aa1a8..7efe7562a03 100644 --- a/htdocs/ticket/history.php +++ b/htdocs/ticket/history.php @@ -40,7 +40,7 @@ $langs->loadLangs(array('companies', 'other', 'ticket')); $id = GETPOST('id', 'int'); $track_id = GETPOST('track_id', 'alpha', 3); $ref = GETPOST('ref', 'alpha'); -$action = GETPOST('action', 'alpha', 3); +$action = GETPOST('action', 'aZ09'); // Security check if (!$user->rights->ticket->read) { diff --git a/htdocs/ticket/index.php b/htdocs/ticket/index.php index 9acfd66c3bb..086ea63bc0e 100644 --- a/htdocs/ticket/index.php +++ b/htdocs/ticket/index.php @@ -35,7 +35,7 @@ $HEIGHT = DolGraph::getDefaultGraphSizeForStats('height'); $id = GETPOST('id', 'int'); $msg_id = GETPOST('msg_id', 'int'); -$action = GETPOST('action', 'alpha', 3); +$action = GETPOST('action', 'aZ09'); if ($user->societe_id) { $socid = $user->societe_id; @@ -184,15 +184,14 @@ if ($result) { $dataseries[] = array('label' => $langs->trans("Assigned"), 'data' => round($tick['assigned'])); $dataseries[] = array('label' => $langs->trans("InProgress"), 'data' => round($tick['inprogress'])); $dataseries[] = array('label' => $langs->trans("Waiting"), 'data' => round($tick['waiting'])); - $dataseries[] = array('label' => $langs->trans("Closed"), 'data' => round($tick['Closed'])); - $dataseries[] = array('label' => $langs->trans("Deleted"), 'data' => round($tick['Deleted'])); + $dataseries[] = array('label' => $langs->trans("Closed"), 'data' => round($tick['closed'])); + $dataseries[] = array('label' => $langs->trans("Deleted"), 'data' => round($tick['deleted'])); } } else { dol_print_error($db); } -$stringtoshow = ''; -$stringtoshow .= '