diff --git a/htdocs/compta/bank/account.php b/htdocs/compta/bank/account.php index d85d6ff3b27..c52ee7a0546 100644 --- a/htdocs/compta/bank/account.php +++ b/htdocs/compta/bank/account.php @@ -5,7 +5,8 @@ * Copyright (C) 2004 Christophe Combelles * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2010-2011 Juanjo Menent - * Copyright (C) 2012 Marcos García + * Copyright (C) 2012 Marcos García + * Copyright (C) 2011-2014 Alexandre Spangaro * * 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 @@ -30,10 +31,12 @@ require('../../main.inc.php'); require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; +require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/sociales/class/chargesociales.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/salaries/class/paymentsalary.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; @@ -133,11 +136,13 @@ if ($action == 'confirm_delete' && $confirm == 'yes' && $user->rights->banque->m llxHeader(); $societestatic=new Societe($db); +$userstatic=new User($db); $chargestatic=new ChargeSociales($db); $memberstatic=new Adherent($db); $paymentstatic=new Paiement($db); $paymentsupplierstatic=new PaiementFourn($db); $paymentvatstatic=new TVA($db); +$paymentsalstatic=new PaymentSalary($db); $bankstatic=new Account($db); $banklinestatic=new AccountLine($db); @@ -458,8 +463,12 @@ if ($id > 0 || ! empty($ref)) } if ($mode_search && ! empty($conf->tax->enabled)) { - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_url as bu2 ON bu2.fk_bank = b.rowid AND bu2.type='payment_vat'"; + // VAT + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_url as bu2 ON bu2.fk_bank = b.rowid AND bu2.type='payment_vat'"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."tva as t ON bu2.url_id = t.rowid"; + // Salary payment + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_url as bu3 ON bu3.fk_bank = b.rowid AND bu3.type='payment_salary'"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."payment_salary as sal ON bu3.url_id = sal.rowid"; } if ($mode_search && ! empty($conf->adherent->enabled)) { @@ -577,6 +586,12 @@ if ($id > 0 || ! empty($ref)) $paymentvatstatic->ref=$links[$key]['url_id']; print ' '.$paymentvatstatic->getNomUrl(2); } + elseif ($links[$key]['type']=='payment_salary') + { + $paymentsalstatic->id=$links[$key]['url_id']; + $paymentsalstatic->ref=$links[$key]['url_id']; + print ' '.$paymentsalstatic->getNomUrl(2); + } elseif ($links[$key]['type']=='banktransfert') { // Do not show link to transfer since there is no transfer card (avoid confusion). Can already be accessed from transaction detail. @@ -611,6 +626,10 @@ if ($id > 0 || ! empty($ref)) elseif ($links[$key]['type']=='company') { + } + elseif ($links[$key]['type']=='user') + { + } elseif ($links[$key]['type']=='member') { @@ -650,6 +669,12 @@ if ($id > 0 || ! empty($ref)) $societestatic->nom=$links[$key]['label']; print $societestatic->getNomUrl(1,'',16); } + else if ($links[$key]['type']=='user') + { + $userstatic->id=$links[$key]['url_id']; + $userstatic->lastname=$links[$key]['label']; + print $userstatic->getNomUrl(1,''); + } else if ($links[$key]['type']=='sc') { // sc=old value diff --git a/htdocs/compta/charges/index.php b/htdocs/compta/charges/index.php index 21d68d42663..aa8d26c32f4 100644 --- a/htdocs/compta/charges/index.php +++ b/htdocs/compta/charges/index.php @@ -2,6 +2,7 @@ /* Copyright (C) 2001-2003 Rodolphe Quiedeville * Copyright (C) 2004-2010 Laurent Destailleur * Copyright (C) 2005-2010 Regis Houssin + * Copyright (C) 2011-2014 Alexandre Spangaro * * 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 @@ -20,13 +21,14 @@ /** * \file htdocs/compta/charges/index.php * \ingroup compta - * \brief Page to list payments of social contributions and vat + * \brief Page to list payments of social contributions, vat and salaries */ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/sociales/class/chargesociales.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/sociales/class/paymentsocialcontribution.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/salaries/class/paymentsalary.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; $langs->load("compta"); @@ -59,6 +61,7 @@ if (! $sortorder) $sortorder="DESC"; $tva_static = new Tva($db); $socialcontrib=new ChargeSociales($db); $payment_sc_static=new PaymentSocialContribution($db); +$sal_static = new PaymentSalary($db); llxHeader('',$langs->trans("TaxAndDividendsArea")); @@ -74,12 +77,92 @@ print_fiche_titre($title, ($year?" if ($year) $param.='&year='.$year; -// Social contributions only if (GETPOST("mode") != 'sconly') { print $langs->trans("DescTaxAndDividendsArea").'
'; print "
"; +} +// Payment Salary +if (empty($_GET["mode"]) || $_GET["mode"] != 'sconly') +{ + $sal = new PaymentSalary($db); + + print_fiche_titre($langs->trans("SalariesPayments").($year?' ('.$langs->trans("Year").' '.$year.')':''), '', ''); + + $sql = "SELECT s.rowid, s.amount, s.label, s.datev as dm"; + $sql.= " FROM ".MAIN_DB_PREFIX."payment_salary as s"; + $sql.= " WHERE s.entity = ".$conf->entity; + if ($year > 0) + { + // Si period renseignee on l'utilise comme critere de date, sinon on prend date echeance, + // ceci afin d'etre compatible avec les cas ou la periode n'etait pas obligatoire + $sql.= " AND s.datev between '".$db->idate(dol_get_first_day($year,1,false))."' AND '".$db->idate(dol_get_last_day($year,12,false))."'"; + } + if (preg_match('/^s/',$sortfield)) $sql.= $db->order($sortfield,$sortorder); + + $result = $db->query($sql); + if ($result) + { + $num = $db->num_rows($result); + $i = 0; + $total = 0 ; + print ''; + print ''; + print_liste_field_titre($langs->trans("PeriodEndDate"),$_SERVER["PHP_SELF"],"s.datev","",$param,'width="120"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Label"),$_SERVER["PHP_SELF"],"s.label","",$param,'',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("ExpectedToPay"),$_SERVER["PHP_SELF"],"s.amount","",$param,'align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("RefPayment"),$_SERVER["PHP_SELF"],"s.rowid","",$param,'',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("DatePayment"),$_SERVER["PHP_SELF"],"s.datev","",$param,'align="center"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("PayedByThisPayment"),$_SERVER["PHP_SELF"],"s.amount","",$param,'align="right"',$sortfield,$sortorder); + print "\n"; + $var=1; + while ($i < $num) + { + $obj = $db->fetch_object($result); + + $total = $total + $obj->amount; + + $var=!$var; + print ""; + print ''."\n"; + + print "\n"; + + print '"; + + // Ref payment + $sal_static->id=$obj->rowid; + $sal_static->ref=$obj->rowid; + print '\n"; + + print '\n"; + print '"; + print "\n"; + + $i++; + } + print ''; + print '"; + print ''; + print ''; + print '"; + print ""; + + print "
'.dol_print_date($db->jdate($obj->dm),'day').'".$obj->label."'.price($obj->amount)."'.$sal_static->getNomUrl(1)."'.dol_print_date($db->jdate($obj->dm),'day')."'.price($obj->amount)."
'.$langs->trans("Total").''.price($total)."  '.price($total)."
"; + $db->free($result); + + print "
"; + } + else + { + dol_print_error($db); + } +} + +// Social contributions only +if (GETPOST("mode") != 'sconly') +{ print_fiche_titre($langs->trans("SocialContributionsPayments").($year?' ('.$langs->trans("Year").' '.$year.')':''), '', ''); } @@ -219,7 +302,7 @@ if (empty($_GET["mode"]) || $_GET["mode"] != 'sconly') $var=!$var; print ""; - print ''.dol_print_date($db->jdate($obj->dm),'day').' ? '."\n"; + print ''.dol_print_date($db->jdate($obj->dm),'day').''."\n"; print "".$obj->label."\n"; diff --git a/htdocs/compta/salaries/class/paymentsalary.class.php b/htdocs/compta/salaries/class/paymentsalary.class.php new file mode 100644 index 00000000000..2aff98a2322 --- /dev/null +++ b/htdocs/compta/salaries/class/paymentsalary.class.php @@ -0,0 +1,578 @@ + + * + * 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/compta/salaries/class/paymentsalary.class.php + * \ingroup tax + * \brief Class for tax module salary payment + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php'; + + +/** + * Put here description of your class + */ +class PaymentSalary extends CommonObject +{ + //public $element='payment_salary'; //!< Id that identify managed objects + //public $table_element='payment_salary'; //!< Name of table without prefix where object is stored + + var $id; + var $ref; + + var $tms; + var $fk_user; + var $datep; + var $datev; + var $amount; + var $label; + var $datesp; + var $dateep; + var $note; + var $fk_bank; + var $fk_user_creat; + var $fk_user_modif; + + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + function __construct($db) + { + $this->db = $db; + $this->element = 'payment_salary'; + $this->table_element = 'payment_salary'; + return 1; + } + + + /** + * Create in database + * + * @param User $user User that create + * @return int <0 if KO, >0 if OK + */ + function create($user) + { + global $conf, $langs; + + $error=0; + + // Clean parameters + $this->fk_user=trim($this->fk_user); + $this->amount=trim($this->amount); + $this->label=trim($this->label); + $this->note=trim($this->note); + $this->fk_bank=trim($this->fk_bank); + $this->fk_user_creat=trim($this->fk_user_creat); + $this->fk_user_modif=trim($this->fk_user_modif); + + // Check parameters + // Put here code to add control on parameters values + + // Insert request + $sql = "INSERT INTO ".MAIN_DB_PREFIX."payment_salary("; + $sql.= "tms,"; + $sql.= "fk_user,"; + $sql.= "datep,"; + $sql.= "datev,"; + $sql.= "amount,"; + $sql.= "label,"; + $sql.= "datesp,"; + $sql.= "dateep,"; + $sql.= "note,"; + $sql.= "fk_bank,"; + $sql.= "fk_user_creat,"; + $sql.= "fk_user_modif"; + + $sql.= ") VALUES ("; + + $sql.= " ".$this->db->idate($this->tms).","; + $sql.= " '".$this->fk_user."',"; + $sql.= " ".$this->db->idate($this->datep).","; + $sql.= " ".$this->db->idate($this->datev).","; + $sql.= " '".$this->amount."',"; + $sql.= " '".$this->label."',"; + $sql.= " ".$this->db->idate($this->datesp).","; + $sql.= " ".$this->db->idate($this->dateep).","; + $sql.= " '".$this->note."',"; + $sql.= " ".($this->fk_bank <= 0 ? "NULL" : "'".$this->fk_bank."'").","; + $sql.= " '".$this->fk_user_creat."',"; + $sql.= " '".$this->fk_user_modif."'"; + + $sql.= ")"; + + dol_syslog(get_class($this)."::create sql=".$sql, LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) + { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."payment_salary"); + + // Appel des triggers + include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + $interface=new Interfaces($this->db); + $result=$interface->run_triggers('PAYMENT_SALARY_CREATE',$this,$user,$langs,$conf); + if ($result < 0) { $error++; $this->errors=$interface->errors; } + // Fin appel triggers + + return $this->id; + } + else + { + $this->error="Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR); + return -1; + } + } + + /** + * Update database + * + * @param User $user User that modify + * @param int $notrigger 0=no, 1=yes (no update trigger) + * @return int <0 if KO, >0 if OK + */ + function update($user=0, $notrigger=0) + { + global $conf, $langs; + + $error=0; + + // Clean parameters + $this->fk_user=trim($this->fk_user); + $this->amount=trim($this->amount); + $this->label=trim($this->label); + $this->note=trim($this->note); + $this->fk_bank=trim($this->fk_bank); + $this->fk_user_creat=trim($this->fk_user_creat); + $this->fk_user_modif=trim($this->fk_user_modif); + + // Check parameters + if (empty($this->fk_user) || $this->fk_user < 0) + { + $this->error='ErrorBadParameter'; + return -1; + } + + // Update request + $sql = "UPDATE ".MAIN_DB_PREFIX."payment_salary SET"; + + $sql.= " tms=".$this->db->idate($this->tms).","; + $sql.= " fk_user='".$this->fk_user."',"; + $sql.= " datep=".$this->db->idate($this->datep).","; + $sql.= " datev=".$this->db->idate($this->datev).","; + $sql.= " amount='".$this->amount."',"; + $sql.= " label='".$this->db->escape($this->label)."',"; + $sql.= " datesp=".$this->db->idate($this->datesp).","; + $sql.= " dateep=".$this->db->idate($this->dateep).","; + $sql.= " note='".$this->db->escape($this->note)."',"; + $sql.= " fk_bank='".$this->fk_bank."',"; + $sql.= " fk_user_creat='".$this->fk_user_creat."',"; + $sql.= " fk_user_modif='".$this->fk_user_modif."'"; + + $sql.= " WHERE rowid=".$this->id; + + dol_syslog(get_class($this)."::update sql=".$sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if (! $resql) + { + $this->error="Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::update ".$this->error, LOG_ERR); + return -1; + } + + if (! $notrigger) + { + // Appel des triggers + include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + $interface=new Interfaces($this->db); + $result=$interface->run_triggers('PAYMENT_SALARY_MODIFY',$this,$user,$langs,$conf); + if ($result < 0) { $error++; $this->errors=$interface->errors; } + // Fin appel triggers + } + + return 1; + } + + + /** + * Load object in memory from database + * + * @param int $id id object + * @param User $user User that load + * @return int <0 if KO, >0 if OK + */ + function fetch($id, $user=0) + { + global $langs; + $sql = "SELECT"; + $sql.= " s.rowid,"; + + $sql.= " s.tms,"; + $sql.= " s.fk_user,"; + $sql.= " s.datep,"; + $sql.= " s.datev,"; + $sql.= " s.amount,"; + $sql.= " s.label,"; + $sql.= " s.datesp,"; + $sql.= " s.dateep,"; + $sql.= " s.note,"; + $sql.= " s.fk_bank,"; + $sql.= " s.fk_user_creat,"; + $sql.= " s.fk_user_modif,"; + $sql.= " b.fk_account,"; + $sql.= " b.fk_type,"; + $sql.= " b.rappro"; + + $sql.= " FROM ".MAIN_DB_PREFIX."payment_salary as s"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON s.fk_bank = b.rowid"; + $sql.= " WHERE s.rowid = ".$id; + + dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) + { + if ($this->db->num_rows($resql)) + { + $obj = $this->db->fetch_object($resql); + + $this->id = $obj->rowid; + $this->ref = $obj->rowid; + $this->tms = $this->db->jdate($obj->tms); + $this->fk_user = $obj->fk_user; + $this->datep = $this->db->jdate($obj->datep); + $this->datev = $this->db->jdate($obj->datev); + $this->amount = $obj->amount; + $this->label = $obj->label; + $this->datesp = $this->db->jdate($obj->datesp); + $this->dateep = $this->db->jdate($obj->dateep); + $this->note = $obj->note; + $this->fk_bank = $obj->fk_bank; + $this->fk_user_creat = $obj->fk_user_creat; + $this->fk_user_modif = $obj->fk_user_modif; + $this->fk_account = $obj->fk_account; + $this->fk_type = $obj->fk_type; + $this->rappro = $obj->rappro; + } + $this->db->free($resql); + + return 1; + } + else + { + $this->error="Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); + return -1; + } + } + + + /** + * Delete object in database + * + * @param User $user User that delete + * @return int <0 if KO, >0 if OK + */ + function delete($user) + { + global $conf, $langs; + + $error=0; + + $sql = "DELETE FROM ".MAIN_DB_PREFIX."payment_salary"; + $sql.= " WHERE rowid=".$this->id; + + dol_syslog(get_class($this)."::delete sql=".$sql); + $resql = $this->db->query($sql); + if (! $resql) + { + $this->error="Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR); + return -1; + } + + // Appel des triggers + include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + $interface=new Interfaces($this->db); + $result=$interface->run_triggers('PAYMENT_SALARY_DELETE',$this,$user,$langs,$conf); + if ($result < 0) { $error++; $this->errors=$interface->errors; } + // Fin appel triggers + + return 1; + } + + + /** + * Initialise an instance with random values. + * Used to build previews or test instances. + * id must be 0 if object instance is a specimen. + * + * @return void + */ + function initAsSpecimen() + { + $this->id=0; + + $this->tms=''; + $this->fk_user=''; + $this->datep=''; + $this->datev=''; + $this->amount=''; + $this->label=''; + $this->datesp=''; + $this->dateep=''; + $this->note=''; + $this->fk_bank=''; + $this->fk_user_creat=''; + $this->fk_user_modif=''; + } + + /** + * Ajoute un paiement de salaire + * + * @param User $user Object user that insert + * @return int <0 if KO, rowid in tva table if OK + */ + function addPayment($user) + { + global $conf,$langs; + + $this->db->begin(); + + // Clean parameters + $this->amount=price2num(trim($this->amount)); + + // Check parameters + if (! $this->label) + { + $this->error=$langs->trans("ErrorFieldRequired",$langs->transnoentities("Label")); + return -3; + } + if ($this->fk_user < 0 || $this->fk_user == '') + { + $this->error=$langs->trans("ErrorFieldRequired",$langs->transnoentities("Person")); + return -4; + } + if ($this->amount < 0 || $this->amount == '') + { + $this->error=$langs->trans("ErrorFieldRequired",$langs->transnoentities("Amount")); + return -5; + } + if (! empty($conf->banque->enabled) && (empty($this->accountid) || $this->accountid <= 0)) + { + $this->error=$langs->trans("ErrorFieldRequired",$langs->transnoentities("Account")); + return -6; + } + if (! empty($conf->banque->enabled) && (empty($this->paymenttype) || $this->paymenttype <= 0)) + { + $this->error=$langs->trans("ErrorFieldRequired",$langs->transnoentities("PaymentMode")); + return -6; + } + + // Insertion dans table des paiement salaires + $sql = "INSERT INTO ".MAIN_DB_PREFIX."payment_salary (fk_user"; + $sql.= ", datep"; + $sql.= ", datev"; + $sql.= ", amount"; + if ($this->note) $sql.=", note"; + if ($this->label) $sql.=", label"; + $sql.= ", datesp"; + $sql.= ", dateep"; + $sql.= ", fk_user_creat"; + $sql.= ", fk_bank"; + $sql.= ", entity"; + $sql.= ") "; + $sql.= " VALUES ("; + $sql.= "'".$this->fk_user."'"; + $sql.= ", '".$this->db->idate($this->datep)."'"; + $sql.= ", '".$this->db->idate($this->datev)."'"; + $sql.= ", ".$this->amount; + if ($this->note) $sql.= ", '".$this->db->escape($this->note)."'"; + if ($this->label) $sql.= ", '".$this->db->escape($this->label)."'"; + $sql.= ", '".$this->db->idate($this->datesp)."'"; + $sql.= ", '".$this->db->idate($this->dateep)."'"; + $sql.= ", '".$user->id."'"; + $sql.= ", NULL"; + $sql.= ", ".$conf->entity; + $sql.= ")"; + + dol_syslog(get_class($this)."::addPayment sql=".$sql); + $result = $this->db->query($sql); + if ($result) + { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."payment_salary"); // TODO devrait s'appeler payment_salary + + // Appel des triggers + include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + $interface=new Interfaces($this->db); + $result=$interface->run_triggers('PAYMENT_SALARY_ADDPAYMENT',$this,$user,$langs,$conf); + if ($result < 0) { $error++; $this->errors=$interface->errors; } + // Fin appel triggers + + if ($this->id > 0) + { + $ok=1; + if (! empty($conf->banque->enabled) && ! empty($this->amount)) + { + // Insertion dans llx_bank + require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + + $acc = new Account($this->db); + $result=$acc->fetch($this->accountid); + if ($result <= 0) dol_print_error($this->db); + + // Insert payment into llx_bank + // Add link 'payment_salary' in bank_url between payment and bank transaction + $bank_line_id = $acc->addline( + $this->datep, + $this->paymenttype, + $this->label, + -abs($this->amount), + '', + '', + $user + ); + + // Mise a jour fk_bank dans llx_paiement. + // On connait ainsi le paiement qui a genere l'ecriture bancaire + if ($bank_line_id > 0) + { + $this->update_fk_bank($bank_line_id); + } + else + { + $this->error=$acc->error; + $ok=0; + } + + // Add link 'payment_salary' in bank_url between payment and bank transaction + $url=DOL_URL_ROOT.'/compta/salaries/fiche.php?id='; + + $result=$acc->add_url_line($bank_line_id, $this->id, $url, "(SalaryPayment)", "payment_salary"); + if ($result <= 0) + { + $this->error=$acc->error; + $ok=0; + } + + // Add link 'user' in bank_url between operation and bank transaction + $linkaddedforthirdparty=array(); + foreach ($this->amounts as $key => $value) + { + $sal = new PaymentSalary($this->db); + + $sal->fetch($key); + $sal->fetch_user($this->fk_user); + + if (! in_array($sal->user->id,$linkaddedforthirdparty)) // Not yet done for this thirdparty + { + $result=$acc->add_url_line( + $bank_line_id, + $sal->user->id, + DOL_URL_ROOT.'/user/fiche.php?id=', + $sal->user->lastname, + 'user' + ); + + if ($result <= 0) dol_print_error($this->db); + $linkaddedforthirdparty[$sal->user->id]=$sal->user->id; // Mark as done for this thirdparty + } + + } + } + + if ($ok) + { + $this->db->commit(); + return $this->id; + } + else + { + $this->db->rollback(); + return -3; + } + } + else + { + $this->error=$this->db->error(); + $this->db->rollback(); + return -2; + } + } + else + { + $this->error=$this->db->error(); + $this->db->rollback(); + return -1; + } + } + + /** + * Mise a jour du lien entre le paiement salaire et la ligne générée dans llx_bank + * + * @param int $id_bank Id compte bancaire + * @return int <0 if KO, >0 if OK + */ + function update_fk_bank($id_bank) + { + $sql = 'UPDATE '.MAIN_DB_PREFIX.'payment_salary SET fk_bank = '.$id_bank; + $sql.= ' WHERE rowid = '.$this->id; + $result = $this->db->query($sql); + if ($result) + { + return 1; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + + /** + * Renvoie nom clicable (avec eventuellement le picto) + * + * @param int $withpicto 0=Pas de picto, 1=Inclut le picto dans le lien, 2=Picto seul + * @param string $option Sur quoi pointe le lien + * @return string Chaine avec URL + */ + function getNomUrl($withpicto=0,$option='') + { + global $langs; + + $result=''; + + $lien = '
'; + $lienfin=''; + + $picto='payment'; + $label=$langs->trans("ShowSalaryPayment").': '.$this->ref; + + if ($withpicto) $result.=($lien.img_object($label,$picto).$lienfin); + if ($withpicto && $withpicto != 2) $result.=' '; + if ($withpicto != 2) $result.=$lien.$this->ref.$lienfin; + return $result; + } + +} +?> diff --git a/htdocs/compta/salaries/fiche.php b/htdocs/compta/salaries/fiche.php new file mode 100644 index 00000000000..636dc8f5002 --- /dev/null +++ b/htdocs/compta/salaries/fiche.php @@ -0,0 +1,345 @@ + + * + * 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/compta/salaries/fiche.php + * \ingroup tax + * \brief Page of salaries payments + */ + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/salaries/class/paymentsalary.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + +$langs->load("compta"); +$langs->load("banks"); +$langs->load("bills"); + +$id=GETPOST("id",'int'); +$action=GETPOST('action'); + +// Security check +$socid = isset($_GET["socid"])?$_GET["socid"]:''; +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'tax', '', '', 'charges'); + +$sal = new PaymentSalary($db); + +// Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array +$hookmanager->initHooks(array('taxsalarycard')); + + + +/** + * Actions + */ + +if ($_POST["cancel"] == $langs->trans("Cancel")) +{ + header("Location: index.php"); + exit; +} + +if ($action == 'add' && $_POST["cancel"] <> $langs->trans("Cancel")) +{ + $db->begin(); + + $datev=dol_mktime(12,0,0, $_POST["datevmonth"], $_POST["datevday"], $_POST["datevyear"]); + $datep=dol_mktime(12,0,0, $_POST["datepmonth"], $_POST["datepday"], $_POST["datepyear"]); + $datesp=dol_mktime(12,0,0, $_POST["datespmonth"], $_POST["datespday"], $_POST["datespyear"]); + $dateep=dol_mktime(12,0,0, $_POST["dateepmonth"], $_POST["dateepday"], $_POST["dateepyear"]); + + + $sal->accountid=$_POST["accountid"]; + $sal->paymenttype=$_POST["paiementtype"]; + $sal->fk_user=$_POST["fk_user"]; + $sal->datev=$datev; + $sal->datep=$datep; + $sal->amount=$_POST["amount"]; + $sal->label=$_POST["label"]; + $sal->datesp=$datesp; + $sal->dateep=$dateep; + + $ret=$sal->addPayment($user); + if ($ret > 0) + { + $db->commit(); + header("Location: index.php"); + exit; + } + else + { + $db->rollback(); + setEventMessage($sal->error, 'errors'); + $action="create"; + } +} + +if ($action == 'delete') +{ + $result=$sal->fetch($id); + + if ($sal->rappro == 0) + { + $db->begin(); + + $ret=$sal->delete($user); + if ($ret > 0) + { + if ($sal->fk_bank) + { + $accountline=new AccountLine($db); + $result=$accountline->fetch($sal->fk_bank); + if ($result > 0) $result=$accountline->delete($user); // $result may be 0 if not found (when bank entry was deleted manually and fk_bank point to nothing) + } + + if ($result >= 0) + { + $db->commit(); + header("Location: ".DOL_URL_ROOT.'/compta/salaries/index.php'); + exit; + } + else + { + $sal->error=$accountline->error; + $db->rollback(); + setEventMessage($sal->error,'errors'); + } + } + else + { + $db->rollback(); + setEventMessage($sal->error,'errors'); + } + } + else + { + setEventMessage('Error try do delete a line linked to a conciliated bank transaction','errors'); + } +} + + +/* +* View +*/ + +llxHeader(); + +$form = new Form($db); + +if ($id) +{ + $salpayment = new PaymentSalary($db); + $result = $salpayment->fetch($id); + if ($result <= 0) + { + dol_print_error($db); + exit; + } +} + +// Formulaire saisie salaire +if ($action == 'create') +{ + $year_current = strftime("%Y",dol_now()); + $pastmonth = strftime("%m",dol_now()) - 1; + $pastmonthyear = $year_current; + if ($pastmonth == 0) + { + $pastmonth = 12; + $pastmonthyear--; + } + + $datesp=dol_mktime(0, 0, 0, $datespmonth, $datespday, $datespyear); + $dateep=dol_mktime(23, 59, 59, $dateepmonth, $dateepday, $dateepyear); + + if (empty($datesp) || empty($dateep)) // We define date_start and date_end + { + $datesp=dol_get_first_day($pastmonthyear,$pastmonth,false); $dateep=dol_get_last_day($pastmonthyear,$pastmonth,false); + } + + print "
\n"; + print ''; + print ''; + + print_fiche_titre($langs->trans("NewSalaryPayment")); + + print ''; + + print ""; + print ''; + + print ''; + + print ""; + print ''; + + // Label + print ''; + + print ""; + print ''; + + print ''; + + // Amount + print ''; + + // Bank + if (! empty($conf->banque->enabled)) + { + print ''; + + print '\n"; + print ""; + } + + // Other attributes + $parameters=array('colspan' => ' colspan="1"'); + $reshook=$hookmanager->executeHooks('formObjectOptions',$parameters,$object,$action); // Note that $action and $object may have been modified by hook + + print '
'.$langs->trans("DatePayment").''; + print $form->select_date($datep,"datep",'','','','add'); + print '
'.$langs->trans("DateValue").''; + print $form->select_date($datev,"datev",'','','','add'); + print '
'.$langs->trans("Person").''; + print $form->select_dolusers(GETPOST('fk_user','int'),'fk_user',1); + print '
'.$langs->trans("Label").'trans("SalaryPayment")).'">
'.$langs->trans("DateStartPeriod").''; + print $form->select_date($datesp,"datesp",'','','','add'); + print '
'.$langs->trans("DateEndPeriod").''; + print $form->select_date($dateep,"dateep",'','','','add'); + print '
'.$langs->trans("Amount").'
'.$langs->trans("Account").''; + $form->select_comptes($_POST["accountid"],"accountid",0,"courant=1",1); // Affiche liste des comptes courant + print '
'.$langs->trans("PaymentMode").''; + $form->select_types_paiements($_POST["paiementtype"], "paiementtype"); + print "
'; + + print "
"; + + print '
  '; + print '
'; + + print '
'; +} + + +/* ************************************************************************** */ +/* */ +/* Barre d'action */ +/* */ +/* ************************************************************************** */ + +if ($id) +{ + $h = 0; + $head[$h][0] = DOL_URL_ROOT.'/compta/salaries/fiche.php?id='.$salpayment->id; + $head[$h][1] = $langs->trans('Card'); + $head[$h][2] = 'card'; + $h++; + + dol_fiche_head($head, 'card', $langs->trans("SalaryPayment"), 0, 'payment'); + + + print ''; + + print ""; + print ''; + + // Person + print ''; + + // Label + print ''; + + print ""; + print ''; + + print ''; + + print ""; + print ''; + + print ''; + + print ''; + + if (! empty($conf->banque->enabled)) + { + if ($salpayment->fk_account > 0) + { + $bankline=new AccountLine($db); + $bankline->fetch($salpayment->fk_bank); + + print ''; + print ''; + print ''; + print ''; + } + } + + // Other attributes + $parameters=array('colspan' => ' colspan="3"'); + $reshook=$hookmanager->executeHooks('formObjectOptions',$parameters,$salpayment,$action); // Note that $action and $object may have been modified by hook + + print '
'.$langs->trans("Ref").''; + print $salpayment->ref; + print '
'.$langs->trans("Person").''; + $usersal=new User($db); + $usersal->fetch($salpayment->fk_user); + print $usersal->getNomUrl(1); + print '
'.$langs->trans("Label").''.$salpayment->label.'
'.$langs->trans("DateStartPeriod").''; + print dol_print_date($salpayment->datesp,'day'); + print '
'.$langs->trans("DateEndPeriod").''; + print dol_print_date($salpayment->dateep,'day'); + print '
'.$langs->trans("DatePayment").''; + print dol_print_date($salpayment->datep,'day'); + print '
'.$langs->trans("DateValue").''; + print dol_print_date($salpayment->datev,'day'); + print '
'.$langs->trans("Amount").''.price($salpayment->amount).'
'.$langs->trans('BankTransactionLine').''; + print $bankline->getNomUrl(1,0,'showall'); + print '
'; + + print ''; + + /* + * Boutons d'actions + */ + print "
\n"; + if ($salpayment->rappro == 0) + { + if (! empty($user->rights->tax->charges->supprimer)) + { + print ''.$langs->trans("Delete").''; + } + else + { + print ''.$langs->trans("Delete").''; + } + } + else + { + print ''.$langs->trans("Delete").''; + } + print "
"; +} + + +$db->close(); + +llxFooter(); +?> diff --git a/htdocs/compta/salaries/index.php b/htdocs/compta/salaries/index.php new file mode 100644 index 00000000000..a7d1b84987d --- /dev/null +++ b/htdocs/compta/salaries/index.php @@ -0,0 +1,107 @@ + + * + * 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/compta/salaries/index.php + * \ingroup tax + * \brief List of salaries payments + */ + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/salaries/class/paymentsalary.class.php'; + +$langs->load("compta"); + +// Security check +$socid = isset($_GET["socid"])?$_GET["socid"]:''; +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'tax', '', '', 'charges'); + + + +/* + * View + */ + +llxHeader(); + +$salstatic = new PaymentSalary($db); +$userstatic = new User($db); + + +print_fiche_titre($langs->trans("SalariesPayments")); + +$sql = "SELECT u.rowid as uid, u.lastname, u.firstname, s.rowid, s.fk_user, s.amount, s.label, s.datev as dm"; +$sql.= " FROM ".MAIN_DB_PREFIX."payment_salary as s, ".MAIN_DB_PREFIX."user as u"; +$sql.= " WHERE u.rowid = s.fk_user"; +$sql.= " AND s.entity = ".$conf->entity; +$sql.= " ORDER BY dm DESC"; + +$result = $db->query($sql); +if ($result) +{ + $num = $db->num_rows($result); + $i = 0; + $total = 0 ; + + print ''; + print ''; + print ''; + print ""; + print ""; + print ''; + print ""; + print "\n"; + $var=1; + while ($i < $num) + { + $obj = $db->fetch_object($result); + $var=!$var; + print ""; + + $userstatic->id=$obj->uid; + $userstatic->lastname=$obj->lastname; + $userstatic->firstname=$obj->firstname; + $salstatic->id=$obj->rowid; + $salstatic->ref=$obj->rowid; + print "\n"; + print "\n"; + print "\n"; + print '\n"; + $total = $total + $obj->amount; + + print ""; + print "\n"; + + $i++; + } + print ''; + print ""; + + print "
'.$langs->trans("Ref").'".$langs->trans("Person")."".$langs->trans("Label")."'.$langs->trans("DatePayment").'".$langs->trans("PayedByThisPayment")."
".$salstatic->getNomUrl(1)."".$userstatic->getNomUrl(1)."".dol_trunc($obj->label,40)."'.dol_print_date($db->jdate($obj->dm),'day')."".price($obj->amount)."
'.$langs->trans("Total").'".price($total)."
"; + $db->free($result); +} +else +{ + dol_print_error($db); +} + + +$db->close(); + +llxFooter(); +?> diff --git a/htdocs/core/menus/init_menu_auguria.sql b/htdocs/core/menus/init_menu_auguria.sql index ea9852253e2..91769af37fb 100644 --- a/htdocs/core/menus/init_menu_auguria.sql +++ b/htdocs/core/menus/init_menu_auguria.sql @@ -180,9 +180,12 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->deplacement->enabled && $leftmenu=="tripsandexpenses"', __HANDLER__, 'left', 2102__+MAX_llx_menu__, 'accountancy', '', 2100__+MAX_llx_menu__, '/compta/deplacement/list.php?leftmenu=tripsandexpenses', 'List', 1, 'trips', '$user->rights->deplacement->lire', '', 0, 2, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->deplacement->enabled && $leftmenu=="tripsandexpenses"', __HANDLER__, 'left', 2103__+MAX_llx_menu__, 'accountancy', '', 2100__+MAX_llx_menu__, '/compta/deplacement/stats/index.php?leftmenu=tripsandexpenses', 'Statistics', 1, 'trips', '$user->rights->deplacement->lire', '', 0, 2, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled', __HANDLER__, 'left', 2200__+MAX_llx_menu__, 'accountancy', 'tax', 6__+MAX_llx_menu__, '/compta/charges/index.php?leftmenu=tax&mainmenu=accountancy', 'MenuTaxAndDividends', 0, 'compta', '$user->rights->tax->charges->lire', '', 0, 6, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled', __HANDLER__, 'left', 2201__+MAX_llx_menu__, 'accountancy', 'tax_social', 2200__+MAX_llx_menu__, '/compta/sociales/index.php?leftmenu=tax_social', 'SocialContributions', 1, '', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_social"', __HANDLER__, 'left', 2202__+MAX_llx_menu__, 'accountancy', '', 2201__+MAX_llx_menu__, '/compta/sociales/charges.php?leftmenu=tax_social&action=create', 'MenuNewSocialContribution', 2, '', '$user->rights->tax->charges->creer', '', 0, 2, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_social"', __HANDLER__, 'left', 2203__+MAX_llx_menu__, 'accountancy', '', 2201__+MAX_llx_menu__, '/compta/charges/index.php?leftmenu=tax_social&mainmenu=accountancy&mode=sconly', 'Payments', 2, '', '$user->rights->tax->charges->lire', '', 0, 3, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled', __HANDLER__, 'left', 2201__+MAX_llx_menu__, 'accountancy', 'tax_sal', 2200__+MAX_llx_menu__, '/compta/salaries/index.php?leftmenu=tax_sal&mainmenu=accountancy', 'Salaries', 1, 'companies', '$user->rights->tax->charges->lire', '', 0, 7, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_sal"', __HANDLER__, 'left', 2201__+MAX_llx_menu__, 'accountancy', '', 2201__+MAX_llx_menu__, '/compta/salaries/fiche.php?leftmenu=tax_sal&action=create', 'NewPayment', 2, 'companies', '$user->rights->tax->charges->creer', '', 0, 0, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_sal"', __HANDLER__, 'left', 2202__+MAX_llx_menu__, 'accountancy', '', 2201__+MAX_llx_menu__, '/compta/salaries/index.php?leftmenu=tax_sal', 'Payments', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled', __HANDLER__, 'left', 2250__+MAX_llx_menu__, 'accountancy', 'tax_social', 2200__+MAX_llx_menu__, '/compta/sociales/index.php?leftmenu=tax_social', 'SocialContributions', 1, '', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_social"', __HANDLER__, 'left', 2251__+MAX_llx_menu__, 'accountancy', '', 2250__+MAX_llx_menu__, '/compta/sociales/charges.php?leftmenu=tax_social&action=create', 'MenuNewSocialContribution', 2, '', '$user->rights->tax->charges->creer', '', 0, 2, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_social"', __HANDLER__, 'left', 2252__+MAX_llx_menu__, 'accountancy', '', 2250__+MAX_llx_menu__, '/compta/charges/index.php?leftmenu=tax_social&mainmenu=accountancy&mode=sconly', 'Payments', 2, '', '$user->rights->tax->charges->lire', '', 0, 3, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS)', __HANDLER__, 'left', 2300__+MAX_llx_menu__, 'accountancy', 'tax_vat', 2200__+MAX_llx_menu__, '/compta/tva/index.php?leftmenu=tax_vat&mainmenu=accountancy', 'VAT', 1, 'companies', '$user->rights->tax->charges->lire', '', 0, 7, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2301__+MAX_llx_menu__, 'accountancy', '', 2300__+MAX_llx_menu__, '/compta/tva/fiche.php?leftmenu=tax_vat&action=create', 'NewPayment', 2, 'companies', '$user->rights->tax->charges->creer', '', 0, 0, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2302__+MAX_llx_menu__, 'accountancy', '', 2300__+MAX_llx_menu__, '/compta/tva/reglement.php?leftmenu=tax_vat', 'Payments', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__); diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 6c7e64d118a..d879d8cde7a 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -804,7 +804,13 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu if (! empty($conf->tax->enabled)) { $newmenu->add("/compta/charges/index.php?leftmenu=tax&mainmenu=accountancy",$langs->trans("MenuTaxAndDividends"), 0, $user->rights->tax->charges->lire, '', $mainmenu, 'tax'); - if (empty($leftmenu) || preg_match('/^tax/i',$leftmenu)) $newmenu->add("/compta/sociales/index.php?leftmenu=tax_social",$langs->trans("MenuSocialContributions"),1,$user->rights->tax->charges->lire); + // Salaries + if (empty($leftmenu) || preg_match('/^tax/i',$leftmenu)) $newmenu->add("/compta/salaries/index.php?leftmenu=tax_sal&mainmenu=accountancy",$langs->trans("Salaries"),1,$user->rights->tax->charges->lire, '', $mainmenu, 'tax_vat'); + if (empty($leftmenu) || preg_match('/^tax/i',$leftmenu)) $newmenu->add("/compta/salaries/fiche.php?leftmenu=tax_sal&action=create",$langs->trans("NewPayment"),2,$user->rights->tax->charges->creer); + if (empty($leftmenu) || preg_match('/^tax/i',$leftmenu)) $newmenu->add("/compta/salaries/index.php?leftmenu=tax_sal",$langs->trans("Payments"),2,$user->rights->tax->charges->lire); + global $mysoc; + // Social contributions + if (empty($leftmenu) || preg_match('/^tax/i',$leftmenu)) $newmenu->add("/compta/sociales/index.php?leftmenu=tax_social",$langs->trans("MenuSocialContributions"),1,$user->rights->tax->charges->lire); if (empty($leftmenu) || preg_match('/^tax/i',$leftmenu)) $newmenu->add("/compta/sociales/charges.php?leftmenu=tax_social&action=create",$langs->trans("MenuNewSocialContribution"), 2, $user->rights->tax->charges->creer); if (empty($leftmenu) || preg_match('/^tax/i',$leftmenu)) $newmenu->add("/compta/charges/index.php?leftmenu=tax_social&mainmenu=accountancy&mode=sconly",$langs->trans("Payments"), 2, $user->rights->tax->charges->lire); // VAT diff --git a/htdocs/install/mysql/migration/3.5.0-3.6.0.sql b/htdocs/install/mysql/migration/3.5.0-3.6.0.sql index b10122ee269..bd37d953875 100644 --- a/htdocs/install/mysql/migration/3.5.0-3.6.0.sql +++ b/htdocs/install/mysql/migration/3.5.0-3.6.0.sql @@ -1036,4 +1036,22 @@ CREATE TABLE IF NOT EXISTS `llx_expeditiondet_batch` ( `qty` double NOT NULL DEFAULT '0', `fk_origin_stock` int(11) NOT NULL, KEY `ix_fk_expeditiondet` (`fk_expeditiondet`) -) ENGINE=InnoDB; \ No newline at end of file +) ENGINE=InnoDB; + +--Salary payment in tax module +CREATE TABLE IF NOT EXISTS `llx_payment_salary` ( + `rowid` integer AUTO_INCREMENT PRIMARY KEY, + `tms` timestamp, + `fk_user` integer NOT NULL, + `datep` date, + `datev` date, + `amount` real NOT NULL DEFAULT 0, + `label` varchar(255), + `datesp` date, -- date de début de la période + `dateep` date, -- date de fin de la période + `entity` integer DEFAULT 1 NOT NULL, -- multi company id + `note` text, + `fk_bank` integer, + `fk_user_creat` integer, + `fk_user_modif` integer +)ENGINE=innodb; \ No newline at end of file diff --git a/htdocs/install/mysql/tables/llx_payment_salary.sql b/htdocs/install/mysql/tables/llx_payment_salary.sql new file mode 100644 index 00000000000..513eed2b943 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_payment_salary.sql @@ -0,0 +1,35 @@ +-- =================================================================== +-- Copyright (C) 2011-2014 Alexandre Spangaro +-- +-- 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 . +-- +-- =================================================================== + +create table llx_payment_salary +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + fk_user integer NOT NULL, + datep date, -- date de paiement + datev date, -- date de valeur + amount real NOT NULL DEFAULT 0, + label varchar(255), + datesp date, -- date de début de la période + dateep date, -- date de fin de la période + entity integer DEFAULT 1 NOT NULL, -- multi company id + note text, + fk_bank integer, + fk_user_creat integer, -- utilisateur qui a cree l'info + fk_user_modif integer -- utilisateur qui a modifié l'info +)ENGINE=innodb; \ No newline at end of file diff --git a/htdocs/langs/de_AT/admin.lang b/htdocs/langs/de_AT/admin.lang index 42aae30343b..678c6fba9bc 100644 --- a/htdocs/langs/de_AT/admin.lang +++ b/htdocs/langs/de_AT/admin.lang @@ -272,7 +272,7 @@ Module210Desc=PostNuke-Integration Module240Name=Datenexport Module240Desc=Werkzeug zum Datenexport(mit Assistenten) Module250Name=Datenimport -Module250Desc=Werkzeug zum Dateinport (mit Assistenten) +Module250Desc=Werkzeug zum Datenimport (mit Assistenten) Module310Name=Mitglieder Module310Desc=Mitgliederverwaltun Module320Name=RSS-Feed diff --git a/htdocs/langs/en_US/compta.lang b/htdocs/langs/en_US/compta.lang index b505efef8bc..03b3f6cbc39 100644 --- a/htdocs/langs/en_US/compta.lang +++ b/htdocs/langs/en_US/compta.lang @@ -41,6 +41,7 @@ VATToCollect=VAT purchases VATSummary=VAT Balance LT2SummaryES=IRPF Balance VATPaid=VAT paid +SalaryPaid=Salary paid LT2PaidES=IRPF Paid LT2CustomerES=IRPF sales LT2SupplierES=IRPF purchases @@ -51,6 +52,7 @@ TaxAndDividendsArea=Tax, social contributions and dividends area SocialContribution=Social contribution SocialContributions=Social contributions MenuTaxAndDividends=Taxes and dividends +MenuSalaries=Salaries MenuSocialContributions=Social contributions MenuNewSocialContribution=New contribution NewSocialContribution=New social contribution @@ -63,19 +65,26 @@ PaymentCustomerInvoice=Customer invoice payment PaymentSupplierInvoice=Supplier invoice payment PaymentSocialContribution=Social contribution payment PaymentVat=VAT payment +PaymentSalary=Salary payment ListPayment=List of payments ListOfPayments=List of payments ListOfCustomerPayments=List of customer payments ListOfSupplierPayments=List of supplier payments DatePayment=Payment date +DateStartPeriod=Date start period +DateEndPeriod=Date end period NewVATPayment=New VAT payment +NewSalaryPayment=New salary payment newLT2PaymentES=New IRPF payment LT2PaymentES=IRPF Payment LT2PaymentsES=IRPF Payments VATPayment=VAT Payment VATPayments=VAT Payments +SalaryPayment=Salary payment +SalariesPayments=Salaries payments SocialContributionsPayments=Social contributions payments ShowVatPayment=Show VAT payment +ShowSalaryPayment=Show salary payment TotalToPay=Total to pay TotalVATReceived=Total VAT received CustomerAccountancyCode=Customer accountancy code diff --git a/htdocs/langs/en_US/productbatch.lang b/htdocs/langs/en_US/productbatch.lang index 09806e2261a..0d530722306 100644 --- a/htdocs/langs/en_US/productbatch.lang +++ b/htdocs/langs/en_US/productbatch.lang @@ -5,3 +5,7 @@ ProductStatusNotOnBatch= Not Managed ProductStatusOnBatchShort= Managed ProductStatusNotOnBatchShort= Not Managed Batch=Batch +atleast1batchfield= Eat-by date or Sell-by date or Batch number +batch_number= Batch number +l_eatby= Eat-by date +l_sellby= Sell-by date diff --git a/htdocs/langs/fr_FR/compta.lang b/htdocs/langs/fr_FR/compta.lang index 4630b76f6e6..dbc1d2520c8 100644 --- a/htdocs/langs/fr_FR/compta.lang +++ b/htdocs/langs/fr_FR/compta.lang @@ -41,6 +41,7 @@ VATToCollect=TVA achats VATSummary=Balance de TVA LT2SummaryES=Balance de IRPF VATPaid=TVA payée +SalaryPaid=Salaire payé LT2PaidES=IRPF Payée LT2CustomerES=IRPF ventes LT2SupplierES=IRPF achats @@ -51,6 +52,7 @@ TaxAndDividendsArea=Espace taxes, charges sociales et dividendes SocialContribution=Charge sociale SocialContributions=Charges sociales MenuTaxAndDividends=Taxes et charges +MenuSalaries=Salaires MenuSocialContributions=Charges sociales MenuNewSocialContribution=Nouvelle charge NewSocialContribution=Nouvelle charge sociale @@ -63,19 +65,26 @@ PaymentCustomerInvoice=Règlement facture client PaymentSupplierInvoice=Règlement facture fournisseur PaymentSocialContribution=Règlement charge sociale PaymentVat=Règlement TVA +PaymentSalary=Règlement salaire ListPayment=Liste des règlements ListOfPayments=Liste des règlements ListOfCustomerPayments=Liste des règlements clients ListOfSupplierPayments=Liste des règlements fournisseurs DatePayment=Date de règlement +DateStartPeriod=Date de début période +DateEndPeriod=Date de fin période NewVATPayment=Nouveau règlement de TVA +NewSalaryPayment=Nouveau règlement de salaire newLT2PaymentES=Nouveau règlement de IRPF LT2PaymentES=Règlement IRPF LT2PaymentsES=Règlements IRPF VATPayment=Règlement TVA VATPayments=Règlements TVA +SalaryPayment=Règlement salaire +SalariesPayments=Règlements des salaires SocialContributionsPayments=Règlements charges sociales -ShowVatPayment=Affiche paiement TVA +ShowVatPayment=Afficher paiement TVA +ShowSalaryPayment=Afficher paiement salaire TotalToPay=Total à payer TotalVATReceived=Total TVA perçue CustomerAccountancyCode=Compte comptable client diff --git a/htdocs/langs/fr_FR/productbatch.lang b/htdocs/langs/fr_FR/productbatch.lang index 24231f0e6e2..0954e74ef16 100644 --- a/htdocs/langs/fr_FR/productbatch.lang +++ b/htdocs/langs/fr_FR/productbatch.lang @@ -5,3 +5,8 @@ ProductStatusNotOnBatch= Non gérer ProductStatusOnBatchShort= Gérer ProductStatusNotOnBatchShort= Non gérer Batch=Lot +atleast1batchfield= DLC ou DLUO ou Numéro de lot +batch_number= Numéro de lot +l_eatby= DLC +l_sellby= DLUO + diff --git a/htdocs/langs/pt_BR/main.lang b/htdocs/langs/pt_BR/main.lang index dcefd12253f..f221a4b8b83 100644 --- a/htdocs/langs/pt_BR/main.lang +++ b/htdocs/langs/pt_BR/main.lang @@ -10,8 +10,8 @@ SeparatorDecimal=, SeparatorThousand=None FormatDateShort=%d/%m/%Y FormatDateShortInput=%d/%m/%Y -FormatDateShortJava=dd/mm/yyyy -FormatDateShortJavaInput=dd/mm/yyyy +FormatDateShortJava=dd/MM/yyyy +FormatDateShortJavaInput=dd/MM/yyyy FormatDateShortJQuery=dd/mm/yy FormatDateShortJQueryInput=dd/mm/yy FormatHourShort=%I:%M %p diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index a84aa8adc5c..b0b0e1f908f 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -29,6 +29,7 @@ * \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'; /** @@ -2796,7 +2797,7 @@ class Product extends CommonObject { $this->stock_reel = 0; - $sql = "SELECT ps.reel, ps.fk_entrepot, ps.pmp"; + $sql = "SELECT ps.reel, ps.fk_entrepot, ps.pmp, ps.rowid"; $sql.= " FROM ".MAIN_DB_PREFIX."product_stock as ps"; $sql.= ", ".MAIN_DB_PREFIX."entrepot as w"; $sql.= " WHERE w.entity IN (".getEntity('warehouse', 1).")"; @@ -2817,6 +2818,7 @@ class Product extends CommonObject $this->stock_warehouse[$row->fk_entrepot] = new stdClass(); $this->stock_warehouse[$row->fk_entrepot]->real = $row->reel; $this->stock_warehouse[$row->fk_entrepot]->pmp = $row->pmp; + if ($this->hasbatch()) $this->stock_warehouse[$row->fk_entrepot]->detail_batch=Productbatch::findAll($this->db,$row->rowid,1); $this->stock_reel+=$row->reel; $i++; } @@ -3320,5 +3322,47 @@ class Product extends CommonObject { return ($this->status_batch == 1 ? true : false); } + + /** + * 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 + * @return int <0 if KO, >0 if OK + */ + function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $dlc='', $dluo='',$lot='') + { + 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); + $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label,'',$dlc,$dluo,$lot); + + if ($result >= 0) + { + $this->db->commit(); + return 1; + } + else + { + dol_print_error($this->db); + $this->db->rollback(); + return -1; + } + } + } } ?> diff --git a/htdocs/product/class/productbatch.class.php b/htdocs/product/class/productbatch.class.php new file mode 100644 index 00000000000..cdfef622f45 --- /dev/null +++ b/htdocs/product/class/productbatch.class.php @@ -0,0 +1,526 @@ + + * Copyright (C) 2013-2014 Cedric GROSS + * + * 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 product/class/productbatch.class.php + * \ingroup productbatch + * \brief Manage record and specific data for batch number management + */ + +require_once(DOL_DOCUMENT_ROOT."/core/class/commonobject.class.php"); + + +/** + * Manage record for batch number management + */ +class Productbatch extends CommonObject +{ + var $element='productbatch'; //!< Id that identify managed objects + private static $_table_element='product_batch'; //!< Name of table without prefix where object is stored + + var $id; + + var $tms=''; + var $fk_product_stock; + var $sellby=''; + var $eatby=''; + var $batch=''; + var $qty; + var $import_key; + + + + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + function __construct($db) + { + $this->db = $db; + return 1; + } + + + /** + * Create object into database + * + * @param User $user User that creates + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + function create($user, $notrigger=0) + { + global $conf, $langs; + $error=0; + + // Clean parameters + $this->clean_param(); + + // Check parameters + // Put here code to add control on parameters values + + // Insert request + $sql = "INSERT INTO ".MAIN_DB_PREFIX.self::$_table_element." ("; + $sql.= "fk_product_stock,"; + $sql.= "sellby,"; + $sql.= "eatby,"; + $sql.= "batch,"; + $sql.= "qty,"; + $sql.= "import_key"; + $sql.= ") VALUES ("; + $sql.= " ".(! isset($this->fk_product_stock)?'NULL':$this->fk_product_stock).","; + $sql.= " ".(! isset($this->sellby) || dol_strlen($this->sellby)==0?'NULL':$this->db->idate($this->sellby)).","; + $sql.= " ".(! isset($this->eatby) || dol_strlen($this->eatby)==0?'NULL':$this->db->idate($this->eatby)).","; + $sql.= " ".(! isset($this->batch)?'NULL':"'".$this->db->escape($this->batch)."'").","; + $sql.= " ".(! isset($this->qty)?'NULL':$this->qty).","; + $sql.= " ".(! isset($this->import_key)?'NULL':"'".$this->db->escape($this->import_key)."'").""; + + + $sql.= ")"; + + $this->db->begin(); + + dol_syslog(get_class($this)."::create sql=".$sql, LOG_DEBUG); + $resql=$this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + if (! $error) + { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.self::$_table_element); + + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($this->db); + //$result=$interface->run_triggers('MYOBJECT_CREATE',$this,$user,$langs,$conf); + //if ($result < 0) { $error++; $this->errors=$interface->errors; } + //// End call triggers + } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return $this->id; + } + } + + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @return int <0 if KO, >0 if OK + */ + function fetch($id) + { + global $langs; + $sql = "SELECT"; + $sql.= " t.rowid,"; + + $sql.= " t.tms,"; + $sql.= " t.fk_product_stock,"; + $sql.= " t.sellby,"; + $sql.= " t.eatby,"; + $sql.= " t.batch,"; + $sql.= " t.qty,"; + $sql.= " t.import_key"; + + + $sql.= " FROM ".MAIN_DB_PREFIX.self::$_table_element." as t"; + $sql.= " WHERE t.rowid = ".$id; + + dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + if ($this->db->num_rows($resql)) + { + $obj = $this->db->fetch_object($resql); + + $this->id = $obj->rowid; + $this->tms = $this->db->jdate($obj->tms); + $this->fk_product_stock = $obj->fk_product_stock; + $this->sellby = $this->db->jdate($obj->sellby); + $this->eatby = $this->db->jdate($obj->eatby); + $this->batch = $obj->batch; + $this->qty = $obj->qty; + $this->import_key = $obj->import_key; + } + $this->db->free($resql); + + return 1; + } + else + { + $this->error = "Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); + return -1; + } + } + + /** + * Update object into database + * + * @param User $user User that modifies + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + function update($user=0, $notrigger=0) + { + global $conf, $langs; + $error=0; + + // Clean parameters + $this->clean_param(); + + // Update request + $sql = "UPDATE ".MAIN_DB_PREFIX.self::$_table_element." SET"; + $sql.= " fk_product_stock=".(isset($this->fk_product_stock)?$this->fk_product_stock:"null").","; + $sql.= " sellby=".(dol_strlen($this->sellby)!=0 ? "'".$this->db->idate($this->sellby)."'" : 'null').","; + $sql.= " eatby=".(dol_strlen($this->eatby)!=0 ? "'".$this->db->idate($this->eatby)."'" : 'null').","; + $sql.= " batch=".(isset($this->batch)?"'".$this->db->escape($this->batch)."'":"null").","; + $sql.= " qty=".(isset($this->qty)?$this->qty:"null").","; + $sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null").""; + $sql.= " WHERE rowid=".$this->id." AND tms='".$this->db->idate($this->tms)."'"; + + $this->db->begin(); + + dol_syslog(get_class($this)."::update sql=".$sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + if (! $error) + { + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($this->db); + //$result=$interface->run_triggers('MYOBJECT_MODIFY',$this,$user,$langs,$conf); + //if ($result < 0) { $error++; $this->errors=$interface->errors; } + //// End call triggers + } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return 1; + } + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + function delete($user, $notrigger=0) + { + global $conf, $langs; + $error=0; + + $this->db->begin(); + + if (! $error) + { + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($this->db); + //$result=$interface->run_triggers('MYOBJECT_DELETE',$this,$user,$langs,$conf); + //if ($result < 0) { $error++; $this->errors=$interface->errors; } + //// End call triggers + } + } + + if (! $error) + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX.self::$_table_element.""; + $sql.= " WHERE rowid=".$this->id; + + dol_syslog(get_class($this)."::delete sql=".$sql); + $resql = $this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return 1; + } + } + + + + /** + * Load an object from its id and create a new one in database + * + * @param int $fromid Id of object to clone + * @return int New id of clone + */ + function createFromClone($fromid) + { + global $user,$langs; + + $error=0; + + $object=new Productbatch($this->db); + + $this->db->begin(); + + // Load source object + $object->fetch($fromid); + $object->id=0; + $object->statut=0; + + // Clear fields + // ... + + // Create clone + $result=$object->create($user); + + // Other options + if ($result < 0) + { + $this->error=$object->error; + $error++; + } + + if (! $error) + { + + + } + + // End + if (! $error) + { + $this->db->commit(); + return $object->id; + } + else + { + $this->db->rollback(); + return -1; + } + } + + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + function initAsSpecimen() + { + $this->id=0; + + $this->tms=''; + $this->fk_product_stock=''; + $this->sellby=''; + $this->eatby=''; + $this->batch=''; + $this->import_key=''; + + + } + + /** + * Clean fields (triming) + * + * @return void + */ + private function clean_param() { + if (isset($this->fk_product_stock)) $this->fk_product_stock=(int) trim($this->fk_product_stock); + if (isset($this->batch)) $this->batch=trim($this->batch); + if (isset($this->qty)) $this->qty=(float) trim($this->qty); + if (isset($this->import_key)) $this->import_key=trim($this->import_key); + } + + /** + * Find first detail record that match eather eat-by or sell-by or batch within given warehouse + * + * @param int $fk_product_stock id product_stock for objet + * @param date $eatby eat-by date for objet + * @param date $sellby sell-by date for objet + * @param string $batch_number batch number for objet + * @return int <0 if KO, >0 if OK + */ + function find($fk_product_stock=0, $eatby='',$sellby='',$batch_number='') + { + global $langs; + $where = array(); + $sql = "SELECT"; + $sql.= " t.rowid,"; + $sql.= " t.tms,"; + $sql.= " t.fk_product_stock,"; + $sql.= " t.sellby,"; + $sql.= " t.eatby,"; + $sql.= " t.batch,"; + $sql.= " t.qty,"; + $sql.= " t.import_key"; + $sql.= " FROM ".MAIN_DB_PREFIX.self::$_table_element." as t"; + $sql.= " WHERE fk_product_stock=".$fk_product_stock; + + if (! empty($eatby)) array_push($where," eatby = '".$this->db->idate($eatby)."'"); + if (! empty($sellby)) array_push($where," sellby = '".$this->db->idate($sellby)."'"); + if (! empty($batch_number)) $sql.= " AND batch = '".$this->db->escape($batch_number)."'"; + + if (! empty($where)) $sql.= " AND (".implode(" OR ",$where).")"; + + dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) + { + if ($this->db->num_rows($resql)) + { + $obj = $this->db->fetch_object($resql); + + $this->id = $obj->rowid; + + $this->tms = $this->db->jdate($obj->tms); + $this->fk_product_stock = $obj->fk_product_stock; + $this->sellby = $this->db->jdate($obj->sellby); + $this->eatby = $this->db->jdate($obj->eatby); + $this->batch = $obj->batch; + $this->qty = $obj->qty; + $this->import_key = $obj->import_key; + } + $this->db->free($resql); + + return 1; + } + else + { + $this->error="Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR); + return -1; + } + } + /** + * Return all batch detail records for given product and warehouse + * + * @param obj $db database object + * @param int $fk_product_stock id product_stock for objet + * @param int $with_qty doesn't return line with 0 quantity + * @return int <0 if KO, >0 if OK + */ + public static function findAll($db,$fk_product_stock,$with_qty=0) + { + global $langs; + $ret = array(); + $sql = "SELECT"; + $sql.= " t.rowid,"; + $sql.= " t.tms,"; + $sql.= " t.fk_product_stock,"; + $sql.= " t.sellby,"; + $sql.= " t.eatby,"; + $sql.= " t.batch,"; + $sql.= " t.qty,"; + $sql.= " t.import_key"; + + + $sql.= " FROM ".MAIN_DB_PREFIX.self::$_table_element." as t"; + $sql.= " WHERE fk_product_stock=".$fk_product_stock; + + if ($with_qty) $sql.= " AND qty<>0"; + dol_syslog("productbatch::findAll sql=".$sql, LOG_DEBUG); + $resql=$db->query($sql); + if ($resql) + { + $num = $db->num_rows($resql); + $i=0; + while ($i < $num) + { + $obj = $db->fetch_object($resql); + + $tmp = new productbatch($db); + $tmp->id = $obj->rowid; + $tmp->tms = $db->jdate($obj->tms); + $tmp->fk_product_stock = $obj->fk_product_stock; + $tmp->sellby = $db->jdate($obj->sellby); + $tmp->eatby = $db->jdate($obj->eatby); + $tmp->batch = $obj->batch; + $tmp->qty = $obj->qty; + $tmp->import_key = $obj->import_key; + + array_push($ret,$tmp); + $i++; + } + $db->free($resql); + + return $ret; + } + else + { + $error="Error ".$db->lasterror(); + dol_syslog("productbatch::find_all ".$error, LOG_ERR); + return -1; + } + } + +} +?> diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 2652c7c45bd..143b9a8466c 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -2,6 +2,7 @@ /* Copyright (C) 2003-2006 Rodolphe Quiedeville * Copyright (C) 2005-2013 Laurent Destailleur * Copyright (C) 2011 Jean Heimburger + * Copyright (C) 2014 Cedric GROSS * * 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 @@ -55,9 +56,13 @@ class MouvementStock * @param int $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 $label Label of stock movement * @param string $datem Force date of movement + * @param date $eatby eat-by date + * @param date $sellby sell-by date + * @param string $batch batch number + * @param boolean $skip_sellby If set to true, stock mouvement is done without impacting batch record * @return int <0 if KO, 0 if fk_product is null, >0 if OK */ - function _create($user, $fk_product, $entrepot_id, $qty, $type, $price=0, $label='', $datem='') + function _create($user, $fk_product, $entrepot_id, $qty, $type, $price=0, $label='', $datem='',$eatby='',$sellby='',$batch='',$skip_sellby=false) { global $conf, $langs; @@ -132,6 +137,7 @@ class MouvementStock $num = 1; $oldqtywarehouse = $obj->reel; $oldpmpwarehouse = $obj->pmp; + $fk_product_stock = $obj->rowid; } $this->db->free($resql); } @@ -193,7 +199,17 @@ class MouvementStock $this->error=$this->db->lasterror(); dol_syslog(get_class($this)."::_create ".$this->error, LOG_ERR); $error = -3; + } else if(empty($fk_product_stock)){ + $fk_product_stock = $this->db->last_insert_id(MAIN_DB_PREFIX."product_stock"); } + + } + + // Update detail stock for sell-by date + if (($product->hasbatch()) && (! $error) && (! $skip_sellby)){ + $param_batch=array('fk_product_stock' =>$fk_product_stock, 'eatby'=>$eatby,'sellby'=>$sellby,'batchnumber'=>$batch); + $result=$this->_create_batch($param_batch, $qty); + if ($result<0) $error++; } if (! $error) @@ -314,9 +330,21 @@ class MouvementStock */ function livraison($user, $fk_product, $entrepot_id, $qty, $price=0, $label='', $datem='') { - return $this->_create($user, $fk_product, $entrepot_id, (0 - $qty), 2, $price, $label, $datem); + return $this->_create($user, $fk_product, $entrepot_id, (0 - $qty), 2, $price, $label, $datem,'','','',true); } + /** + * Decrease stock for batch record + * + * @param int $id_stock_dluo Id product_dluo + * @param int $qty Quantity + * @return int <0 if KO, >0 if OK + */ + function livraison_batch($id_stock_dluo, $qty) + { + $ret=$this->_create_batch($id_stock_dluo, (0 - $qty)); + return $ret; + } /** * Increase stock for product and subproducts @@ -327,11 +355,14 @@ class MouvementStock * @param int $qty Quantity * @param int $price Price * @param string $label Label of stock movement + * @param date $eatby eat-by date + * @param date $sellby sell-by date + * @param string $batch batch number * @return int <0 if KO, >0 if OK */ - function reception($user, $fk_product, $entrepot_id, $qty, $price=0, $label='') + function reception($user, $fk_product, $entrepot_id, $qty, $price=0, $label='', $eatby='', $sellby='', $batch='') { - return $this->_create($user, $fk_product, $entrepot_id, $qty, 3, $price, $label); + return $this->_create($user, $fk_product, $entrepot_id, $qty, 3, $price, $label, '', $eatby, $sellby, $batch); } @@ -384,6 +415,59 @@ class MouvementStock return -1; } } + + /** + * Create or update batch record + * + * @param variant $dluo Could be either int if id of product_batch or array with at leat fk_product_stock + * @param int $qty Quantity of product with batch number + * @return int <0 if KO, else return productbatch id + */ + function _create_batch($dluo, $qty ) { + $pdluo=New Productbatch($this->db); + + //Try to find an existing record with batch same batch number or id + if (is_numeric($dluo)) { + $result=$pdluo->fetch($dluo); + } else if (is_array($dluo)) { + if (isset($dluo['fk_product_stock'])) { + $vfk_product_stock=$dluo['fk_product_stock']; + $veatby = $dluo['eatby']; + $vsellby = $dluo['sellby']; + $vbatchnumber = $dluo['batchnumber']; + $result = $pdluo->find($vfk_product_stock,$veatby,$vsellby,$vbatchnumber); + } else { + dol_syslog(get_class($this)."::_create_batch array param dluo must contain at least key fk_product_stock".$error, LOG_ERR); + $result = -1; + } + } else { + dol_syslog(get_class($this)."::_create_batch error invalid param dluo".$error, LOG_ERR); + $result = -1; + } + + //batch record found so we update it + if ($result>0) { + if ($pdluo->id >0) { + $pdluo->qty +=$qty; + if ($pdluo->qty == 0) { + $result=$pdluo->delete(0,1); + } else { + $result=$pdluo->update(0,1); + } + } else { + $pdluo->fk_product_stock=$vfk_product_stock; + $pdluo->qty = $qty; + $pdluo->eatby = $veatby; + $pdluo->sellby = $vsellby; + $pdluo->batch = $vbatchnumber; + $result=$pdluo->create(0,1); + } + return $result; + } else { + return -1; + } + + } } ?> diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index 3d7acc0661b..1fd34c65c2d 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -6,6 +6,7 @@ * Copyright (C) 2005-2009 Regis Houssin * Copyright (C) 2013 Cédric Salvador * Copyright (C) 2013 Juanjo Menent + * Copyright (C) 2014 Cédric Gross * * 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 @@ -32,11 +33,14 @@ require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; +if (! empty($conf->productbatch->enabled)) require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php'; $langs->load("products"); $langs->load("orders"); $langs->load("bills"); $langs->load("stocks"); +if (! empty($conf->productbatch->enabled)) $langs->load("productbatch"); + $action=GETPOST("action"); $cancel=GETPOST('cancel'); @@ -99,14 +103,42 @@ if ($action == "correct_stock" && ! $cancel) $action='correction'; } + if (! empty($conf->productbatch->enabled)) + { + $product = new Product($db); + $result=$product->fetch($id); + + if ($product->hasbatch() && (! GETPOST("sellby")) && (! GETPOST("eatby")) && (! GETPOST("batch_number"))) { + setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("atleast1batchfield")), 'errors'); + $error++; + $action='correction'; + } + } + if (! $error) { $priceunit=price2num(GETPOST("price")); if (is_numeric(GETPOST("nbpiece")) && $id) { + if (empty($product)) { $product = new Product($db); $result=$product->fetch($id); - + } + if ($product->hasbatch()) { + $d_eatby=dol_mktime(12, 0, 0, $_POST['eatbymonth'], $_POST['eatbyday'], $_POST['eatbyyear']); + $d_sellby=dol_mktime(12, 0, 0, $_POST['sellbymonth'], $_POST['sellbyday'], $_POST['sellbyyear']); + $result=$product->correct_stock_batch( + $user, + GETPOST("id_entrepot"), + GETPOST("nbpiece"), + GETPOST("mouvement"), + GETPOST("label"), + $priceunit, + $d_eatby, + $d_sellby, + GETPOST('batch_number') + ); // We do not change value of stock for a correction + } else { $result=$product->correct_stock( $user, GETPOST("id_entrepot"), @@ -115,6 +147,7 @@ if ($action == "correct_stock" && ! $cancel) GETPOST("label"), $priceunit ); // We do not change value of stock for a correction + } if ($result > 0) { @@ -246,6 +279,12 @@ if ($id > 0 || $ref) print $product->getLibStatut(2,1); print ''; + if ($conf->productbatch->enabled) { + print ''.$langs->trans("Status").' ('.$langs->trans("l_sellby").')'; + print $product->getLibStatut(2,2); + print ''; + } + // PMP print ''.$langs->trans("AverageUnitPricePMP").''; print ''.price($product->pmp).' '.$langs->trans("HT").''; @@ -405,7 +444,7 @@ if ($id > 0 || $ref) // Warehouse print ''; - print ''.$langs->trans("Warehouse").''; + print ''.$langs->trans("Warehouse").''; print ''; print $formproduct->selectWarehouses(($_GET["dwid"]?$_GET["dwid"]:GETPOST('id_entrepot')),'id_entrepot','',1); print ''; @@ -419,13 +458,26 @@ if ($id > 0 || $ref) // Label print ''; - print ''.$langs->trans("Label").''; + print ''.$langs->trans("Label").''; print ''; print ''; print ''; print ''.$langs->trans("UnitPurchaseValue").''; print ''; + //eat-by date + if ((! empty($conf->productbatch->enabled)) && $product->hasbatch()) { + print ''; + print ''.$langs->trans("l_eatby").''; + $form->select_date('','eatby','','',1,""); + print ''; + print ''.$langs->trans("l_sellby").''; + $form->select_date('','sellby','','',1,""); + print ''; + print ''.$langs->trans("batch_number").''; + print ''; + print ''; + } print ''; print '
 '; @@ -513,7 +565,7 @@ if (empty($action) && $product->id) print ''.$langs->trans("StockCorrection").''; } - if ($user->rights->stock->mouvement->creer) + if (($user->rights->stock->mouvement->creer) && !$product->hasbatch()) { print ''.$langs->trans("StockMovement").''; } @@ -528,15 +580,22 @@ if (empty($action) && $product->id) * Contenu des stocks */ print '
'; -print ''; +print ''; print ''; print ''; print ''; print ''; print ''; print ''; +if ( (! empty($conf->productbatch->enabled)) && $product->hasbatch()) { + print ''; + print ''; + print ''; + print ''; + print ''; +} -$sql = "SELECT e.rowid, e.label, ps.reel, ps.pmp"; +$sql = "SELECT e.rowid, e.label, ps.reel, ps.pmp, ps.rowid as product_stock_id"; $sql.= " FROM ".MAIN_DB_PREFIX."entrepot as e,"; $sql.= " ".MAIN_DB_PREFIX."product_stock as ps"; $sql.= " WHERE ps.reel != 0"; @@ -561,7 +620,7 @@ if ($resql) $entrepotstatic->id=$obj->rowid; $entrepotstatic->libelle=$obj->label; print ''; - print ''; + print ''; print ''; // PMP print ''; // Ditto : Show PMP from movement or from product @@ -579,12 +638,24 @@ if ($resql) if (price2num($obj->pmp)) $totalwithpmp += $obj->reel; $totalvalue = $totalvalue + price2num($obj->pmp*$obj->reel,'MU'); // Ditto : Show PMP from movement or from product $totalvaluesell = $totalvaluesell + price2num($product->price*$obj->reel,'MU'); // Ditto : Show PMP from movement or from product + //Batch Detail + if ((! empty($conf->productbatch->enabled)) && $product->hasbatch()) { + $details=Productbatch::findAll($db,$obj->product_stock_id); + if ($details<0) dol_print_error($db); + foreach ($details as $pdluo) { + print "\n".''; + print ''; + print ''; + print ''; + print ''; + } + } $i++; $var=!$var; } } else dol_print_error($db); -print ''; +print ''; print ''; print '
'.$langs->trans("Warehouse").'
'.$langs->trans("Warehouse").''.$langs->trans("NumberOfUnit").''.$langs->trans("AverageUnitPricePMPShort").''.$langs->trans("EstimatedStockValueShort").''.$langs->trans("SellPriceMin").''.$langs->trans("EstimatedStockValueSellShort").'
'.$langs->trans("l_eatby").''.$langs->trans("l_sellby").''.$langs->trans("batch_number").'
'.$entrepotstatic->getNomUrl(1).''.$entrepotstatic->getNomUrl(1).''.$obj->reel.($obj->reel<0?' '.img_warning():'').''.(price2num($obj->pmp)?price2num($obj->pmp,'MU'):'').'
'. dol_print_date($pdluo->eatby,'day') .''. dol_print_date($pdluo->sellby,'day') .''.$pdluo->batch.''.$pdluo->qty.($pdluo->qty<0?' '.img_warning():'').'
'.$langs->trans("Total").':
'.$langs->trans("Total").':'.$total.''; print ($totalwithpmp?price($totalvalue/$totalwithpmp):' ');