Finish cash control feature of v23

This commit is contained in:
Laurent Destailleur
2026-01-05 00:18:43 +01:00
parent c1a9c5469a
commit 362f37d783
13 changed files with 403 additions and 121 deletions

View File

@@ -505,7 +505,7 @@ if (GETPOST('action') == 'export' && $user->hasRight('blockedlog', 'read')) { /
$sql = "SELECT action, module_source, object_format, MIN(date_creation) as datemin, SUM(amounts_taxexcl) as sumamounts_taxexcl, SUM(amounts) as sumamounts";
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
$sql .= " WHERE entity = ".((int) $conf->entity);
//$sql .= " AND action IN ('BILL_VALIDATE', 'BILL_SENTBYMAIL', 'PAYMENT_CUSTOMER_CREATE', 'CASHCONTROL_VALIDATE', 'PAYMENT_CUSTOMER_DELETE', 'DOC_DOWNLOAD', 'DOC_PREVIEW')";
//$sql .= " AND action IN ('BILL_VALIDATE', 'BILL_SENTBYMAIL', 'PAYMENT_CUSTOMER_CREATE', 'CASHCONTROL_CLOSE', 'PAYMENT_CUSTOMER_DELETE', 'DOC_DOWNLOAD', 'DOC_PREVIEW')";
$sql .= " AND action IN ('BILL_VALIDATE', 'PAYMENT_CUSTOMER_CREATE', 'PAYMENT_CUSTOMER_DELETE')"; // Only event into lifetime total
//$sql .= " AND action IN ('PAYMENT_CUSTOMER_CREATE')";
$sql .= " GROUP BY action, module_source, object_format";

View File

@@ -295,8 +295,10 @@ class BlockedLog
$sep++;
$this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("CashControl").'</span>', 'disabled' => 1);
}
$this->trackedevents['CASHCONTROL_VALIDATE'] = array('id' => 'CASHCONTROL_VALIDATE', 'label' => 'logCASHCONTROL_VALIDATE', 'labelhtml' => img_picto('', 'pos', 'class="pictofixedwidth").').$langs->trans('logCASHCONTROL_VALIDATE'));
if (getDolGlobalString('BLOCKEDLOG_ADD_OLD_CASHCONTROL_VALIDATE')) {
$this->trackedevents['CASHCONTROL_VALIDATE'] = array('id' => 'CASHCONTROL_VALIDATE', 'label' => 'logCASHCONTROL_VALIDATE', 'labelhtml' => img_picto('', 'pos', 'class="pictofixedwidth").').$langs->trans('logCASHCONTROL_VALIDATE'));
}
$this->trackedevents['CASHCONTROL_CLOSE'] = array('id' => 'CASHCONTROL_CLOSE', 'label' => 'logCASHCONTROL_CLOSE', 'labelhtml' => img_picto('', 'pos', 'class="pictofixedwidth").').$langs->trans('logCASHCONTROL_CLOSE'));
}
// Add more action to track from a conf variable. For the case we want to track other actions into the unalterable log.

View File

@@ -1623,7 +1623,7 @@ class Categorie extends CommonObject
$w[] = '<a class="valignmiddle '.($i < count($way) ? 'small ' : '').$forced_color.'" href="'.DOL_URL_ROOT.'/'.$url.'?catid='.((int) $cat->id).'">'.($addpicto ? img_object('', 'category') : '').$cat->label.'</a>';
}
}
$newcategwithpath = preg_replace('/colortoreplace/', $forced_color, implode('<span class="inline-block valignmiddle paddingleft paddingright '.$forced_color.'">'.$sep.'</span>', $w));
$newcategwithpath = preg_replace('/colortoreplace/', $forced_color, implode('<span class="inline-block valignmiddle paddingleft paddingright small '.$forced_color.'">'.$sep.'</span>', $w));
$ways[] = $newcategwithpath;
}

View File

@@ -52,13 +52,9 @@ $backtopage = GETPOST('backtopage', 'aZ09');
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$categid = GETPOST('categid');
$label = GETPOST("label");
$now = dol_now();
$syear = (GETPOSTISSET('closeyear') ? GETPOSTINT('closeyear') : dol_print_date($now, "%Y"));
$smonth = (GETPOSTISSET('closemonth') ? GETPOSTINT('closemonth') : dol_print_date($now, "%m"));
$sday = (GETPOSTISSET('closeday') ? GETPOSTINT('closeday') : dol_print_date($now, "%d"));
$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
$sortfield = GETPOST('sortfield', 'aZ09comma');
@@ -118,6 +114,75 @@ $permissiontoadd = ($user->hasRight("cashdesk", "run") || $user->hasRight("takep
$permissiontodelete = ($user->hasRight("cashdesk", "run") || $user->hasRight("takepos", "run")) || ($permissiontoadd && $object->status == 0);
// Must be after the fetch
$datestart = null;
$dateend = null;
$syear = (GETPOSTISSET('closeyear') ? GETPOSTINT('closeyear') : dol_print_date($now, "%Y", 'tzuserrel'));
$smonth = (GETPOSTISSET('closemonth') ? GETPOSTINT('closemonth') : dol_print_date($now, "%m", 'tzuserrel'));
$sday = (GETPOSTISSET('closeday') ? GETPOSTINT('closeday') : dol_print_date($now, "%d", 'tzuserrel'));
// TODO Add a global option to define the end hours when doing a cash control
$shour = 0;
$smin = 0;
$ssec = 0;
if ($object->id > 0) {
// When object is know, we must define first the end date (stored in database with different components) and deduct the start date
if (empty($object->day_close) && !empty($object->month_close)) {
$dateend = dol_mktime($object->hour_close, $object->min_close, $object->sec_close, $object->month_close, $object->day_close, $object->year_close, 'gmt');
$datestart = dol_time_plus_duree($dateend, -1, 'y', 0);
} elseif (empty($object->day_close) && empty($object->month_close)) {
$dateend = dol_mktime($object->hour_close, $object->min_close, $object->sec_close, 12, 31, $object->year_close, 'gmt');
$datestart = dol_mktime($object->hour_close, $object->min_close, $object->sec_close, 12, 1, $object->year_close, 'gmt');
$datestart = dol_time_plus_duree($datestart, -1, 'm', 0);
} else {
$dateend = dol_mktime($object->hour_close, $object->min_close, $object->sec_close, $object->month_close, $object->day_close, $object->year_close, 'gmt');
$datestart = dol_time_plus_duree($dateend, -1, 'd', 0);
}
$datestart += 1; // Add 1 second
} else {
if ($syear && !$smonth) {
$datestart = dol_get_first_day($syear, 1, 'tzuserrel');
$dateend = dol_get_last_day($syear, 12, 'tzuserrel');
$sql .= " AND dateo < '".$db->idate($datestart)."'";
} elseif ($syear && $smonth && !$sday) {
$datestart = dol_get_first_day($syear, $smonth, 'tzuserrel');
$dateend = dol_get_last_day($syear, $smonth, 'tzuserrel');
$sql .= " AND dateo < '".$db->idate($datestart)."'";
} elseif ($syear && $smonth && $sday) {
$datestart = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, 'tzuserrel');
$dateend = dol_mktime(23, 59, 59, $smonth, $sday, $syear, 'tzuserrel');
$sql .= " AND dateo < '".$db->idate($datestart)."'";
} else {
setEventMessages($langs->trans('YearNotDefined'), null, 'errors');
}
}
//var_dump(dol_print_date($datestart, 'dayhour', 'gmt'), dol_print_date($dateend, 'dayhour', 'gmt'));
// Define dates and terminal
$terminalid = '';
$terminaltouse = '';
if ($action == "create" || $action == "start" || $action == 'valid' || $action == 'close') {
if ($action == 'valid' || $action == 'close') {
$posmodule = $object->posmodule;
$terminalid = $object->posnumber;
$terminaltouse = $terminalid;
$syear = $object->year_close;
$smonth = $object->month_close;
$sday = $object->day_close;
} elseif (GETPOST('posnumber', 'alpha') != '' && GETPOST('posnumber', 'alpha') != '-1') {
$posmodule = GETPOST('posmodule', 'alpha');
$terminalid = GETPOST('posnumber', 'alpha');
$terminaltouse = $terminalid;
if ($terminaltouse == '1' && $posmodule == 'cashdesk') {
$terminaltouse = '';
}
}
}
/*
* Actions
*/
@@ -179,13 +244,17 @@ if ($action == "start" && $permissiontoadd) {
}
if (!$error) {
$dateclosegmt = dol_mktime(GETPOSTISSET('closehour') ? GETPOSTINT('closehour') : 23, GETPOSTISSET('closemin') ? GETPOSTINT('closemin') : 59, GETPOSTISSET('closesec') ? GETPOSTINT('closesec') : 59, GETPOSTINT('closemonth'), GETPOSTINT('closeday'), GETPOSTINT('closeyear'), 'tzuserrel');
dol_syslog('The closing date must be '.dol_print_date($dateclosegmt, 'standard', 'gmt').' UTC');
if (GETPOSTINT('closeday')) {
$dateclosegmt = dol_mktime(GETPOSTISSET('closehour') ? GETPOSTINT('closehour') : 23, GETPOSTISSET('closemin') ? GETPOSTINT('closemin') : 59, GETPOSTISSET('closesec') ? GETPOSTINT('closesec') : 59, GETPOSTINT('closemonth') ? GETPOSTINT('closemonth') : 12, GETPOSTINT('closeday'), GETPOSTINT('closeyear'), 'tzuserrel');
} else {
$dateclosegmt = dol_mktime(GETPOSTISSET('closehour') ? GETPOSTINT('closehour') : 23, GETPOSTISSET('closemin') ? GETPOSTINT('closemin') : 59, GETPOSTISSET('closesec') ? GETPOSTINT('closesec') : 59, GETPOSTINT('closemonth') ? GETPOSTINT('closemonth') : 12, 15, GETPOSTINT('closeyear'), 'tzuserrel');
}
dol_syslog('The closing date will be '.dol_print_date($dateclosegmt, 'standard', 'gmt').' UTC');
$tmparray = dol_getdate($dateclosegmt, false, 'gmt');
$object->day_close = $tmparray['mday'];
$object->month_close = $tmparray['mon'];
$object->day_close = GETPOSTINT('closeday') ? $tmparray['mday'] : null;
$object->month_close = GETPOSTINT('closemonth') ? $tmparray['mon'] : null;
$object->year_close = $tmparray['year'];
$object->hour_close = $tmparray['hours'];
@@ -222,26 +291,126 @@ if ($action == "valid" && $permissiontoadd) { // validate = close
$db->begin();
/*
$object->day_close = GETPOST('closeday', 'int');
$object->month_close = GETPOST('closemonth', 'int');
$object->year_close = GETPOST('closeyear', 'int');
*/
// Save the calculated amount
// It will also be saved automatically into llx_blockedlog by the trigger in valid().
$object->cash = (float) price2num(GETPOST('cash_calculated', 'alpha'));
$object->card = (float) price2num(GETPOST('card_calculated', 'alpha'));
$object->cheque = (float) price2num(GETPOST('cheque_calculated', 'alpha'));
// Save the real amount in llx_pos_cash_fence.
// It will also be saved automatically into llx_blockedlog by the trigger in valid().
$object->cash = (float) price2num(GETPOST('cash_amount', 'alpha'));
$object->card = (float) price2num(GETPOST('card_amount', 'alpha'));
$object->cheque = (float) price2num(GETPOST('cheque_amount', 'alpha'));
$object->cash_declared = (float) price2num(GETPOST('cash_amount', 'alpha'));
$object->card_declared = (float) price2num(GETPOST('card_amount', 'alpha'));
$object->cheque_declared = (float) price2num(GETPOST('cheque_amount', 'alpha'));
// TODO Add perpetual amount
// Add also perpetual amount into cash_lifetime, card_lifetime, cheque_lifetime
$cash_lifetime = $card_lifetime = $cheque_lifetime = 0;
//$dates = $datestart;
$datee = $dateend;
$datefilter = 'p.datep';
$modulesourcefilter = 'f.module_source';
$amountfield = 'pf.amount';
$joinleft = 'LEFT ';
if (isALNERunningVersion() && $mysoc->country_code == 'FR') {
$datefilter = 'bl.date_creation'; // By using this as a filter, it is like the LEFT JOIN is an INNER JOIN
$modulesourcefilter = 'bl.module_source';
$amountfield = 'bl.amounts';
$joinleft = '';
}
$lifetimeamount = array();
$lifetimenb = array();
// Calculate $theoricalamountforterminal at end of period
// Sum of payment + Initial amount in bank
foreach ($arrayofpaymentmode as $key => $val) {
// NOTE: Must be same request than into report.php, except it does an aggregate and do the request 3 times, once per payment type.
/*$sql = "SELECT p.rowid, p.datep as datep, cp.code,";
$sql .= " f.rowid as facid, f.ref, f.datef as datef, pf.amount as amount,";
$sql .= " b.fk_account as bankid,";
$sql .= " bl.signature"; */
$sql = "SELECT SUM(".$db->sanitize($amountfield).") as total, COUNT(*) as nb";
$sql .= " FROM ".MAIN_DB_PREFIX."paiement_facture as pf, ".MAIN_DB_PREFIX."facture as f,";
$sql .= " ".MAIN_DB_PREFIX."paiement as p";
//$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."blockedlog as bl ON bl.ref_object = p.ref AND bl.entity = ".((int) $conf->entity).",";
$sql .= " ".$joinleft." JOIN ".MAIN_DB_PREFIX."blockedlog as bl ON bl.action = 'PAYMENT_CUSTOMER_CREATE'";
$sql .= " AND bl.element = 'payment' AND bl.fk_object = p.rowid AND bl.entity = ".((int) $conf->entity).",";
//$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON p.fk_bank = b.rowid,";
$sql .= " ".MAIN_DB_PREFIX."c_paiement as cp";
$sql .= " WHERE pf.fk_facture = f.rowid AND p.rowid = pf.fk_paiement AND cp.id = p.fk_paiement";
$sql .= " AND ".$db->sanitize($modulesourcefilter)." = '".$db->escape($posmodule)."'";
$sql .= " AND f.pos_source = '".$db->escape($terminalid)."'";
$sql .= " AND p.entity = ".((int) $conf->entity); // Never share entities for features related to accountancy
$sql .= " AND ".$db->sanitize($datefilter)." <= '".$db->idate($datee)."'";
if ($key == 'cash') {
$sql .= " AND cp.code = 'LIQ'";
} elseif ($key == 'cheque') {
$sql .= " AND cp.code = 'CHQ'";
} elseif ($key == 'card') {
$sql .= " AND cp.code = 'CB'";
} else {
dol_print_error(null, 'Value for key = '.$key.' not supported');
exit;
}
//print $sql."<br>\n";
$resql = $db->query($sql);
if ($resql) {
$lifetimeamount[$terminalid][$key] = 0;
$lifetimenb[$terminalid][$key] = 0;
$obj = $db->fetch_object($resql);
if ($obj) {
$lifetimeamount[$terminalid][$key] = price2num($lifetimeamount[$terminalid][$key] + $obj->total);
$lifetimenb[$terminalid][$key] = $obj->nb;
}
} else {
dol_print_error($db);
}
}
$cash_lifetime = $lifetimeamount[$terminalid]['cash'];
$card_lifetime = $lifetimeamount[$terminalid]['card'];
$cheque_lifetime = $lifetimeamount[$terminalid]['cheque'];
$object->cash_lifetime = $cash_lifetime;
$object->card_lifetime = $card_lifetime;
$object->cheque_lifetime = $cheque_lifetime;
// Get the date of first record for the lifetime calculation
$sql = "SELECT action, module_source, object_format, date_creation";
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
$sql .= " WHERE entity = ".((int) $conf->entity);
$sql .= " AND action IN ('PAYMENT_CUSTOMER_CREATE')"; // Only this event
$sql .= " AND module_source = '".$db->escape($posmodule)."'";
//$sql .= " AND pos_source = '".$db->escape($terminalid)."'";
$sql .= $db->order("date_creation", "ASC");
$sql .= $db->plimit(1);
$firstrecorddate = 0;
$resql = $db->query($sql);
if ($resql) {
$obj = $db->fetch_object($resql);
if ($obj) {
$firstrecorddate = $obj->date_creation;
}
}
if ($firstrecorddate) {
$object->lifetime_start = $firstrecorddate;
}
$result = $object->update($user);
$result = $object->valid($user); // This also save data into the Unalterable Log table by the trigger CASHCONTROL_VALIDATE.
$result2 = $object->close($user); // This also save data into the Unalterable Log table by the trigger CASHCONTROL_CLOSE.
if ($result <= 0) {
// TODO
// Add an entry into bank to fix difference between amount and declared and if user ask it with a checkbox ?
if ($result <= 0 || $result2 <= 0) {
setEventMessages($object->error, $object->errors, 'errors');
$db->rollback();
} else {
@@ -294,16 +463,6 @@ $initialbalanceforterminal = array();
$theoricalamountforterminal = array();
$theoricalnbofinvoiceforterminal = array();
$terminalid = '';
$terminaltouse = '';
// TODO Ask hours to use for the range date
$shour = 0;
$smin = 0;
$ssec = 0;
$datestart = null;
$dateend = null;
llxHeader('', $langs->trans("CashControl"));
@@ -351,21 +510,6 @@ if ($action == "create" || $action == "start" || $action == 'close') {
if ($bankid > 0) {
$sql = "SELECT SUM(amount) as total FROM ".MAIN_DB_PREFIX."bank";
$sql .= " WHERE fk_account = ".((int) $bankid);
if ($syear && !$smonth) {
$datestart = dol_get_first_day($syear, 1);
$dateend = dol_get_last_day($syear, 12);
$sql .= " AND dateo < '".$db->idate($datestart)."'";
} elseif ($syear && $smonth && !$sday) {
$datestart = dol_get_first_day($syear, $smonth);
$dateend = dol_get_last_day($syear, $smonth);
$sql .= " AND dateo < '".$db->idate($datestart)."'";
} elseif ($syear && $smonth && $sday) {
$datestart = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear);
$dateend = dol_mktime(23, 59, 59, $smonth, $sday, $syear);
$sql .= " AND dateo < '".$db->idate($datestart)."'";
} else {
setEventMessages($langs->trans('YearNotDefined'), null, 'errors');
}
$resql = $db->query($sql);
if ($resql) {
@@ -384,17 +528,6 @@ if ($action == "create" || $action == "start" || $action == 'close') {
$dates = $datestart;
$datee = $dateend;
/*
if ($syear && !$smonth) {
$dates = dol_get_first_day($syear, 1); $datee = dol_get_last_day($syear, 12);
} elseif ($syear && $smonth && !$sday) {
$dates = dol_get_first_day($syear, $smonth); $datee = dol_get_last_day($syear, $smonth);
} elseif ($syear && $smonth && $sday) {
$dates = dol_mktime(0, 0, 0, $smonth, $sday, $syear); $datee = dol_mktime(23, 59, 59, $smonth, $sday, $syear);
} else {
dol_print_error(null, 'Year not defined');
}
*/
$datefilter = 'p.datep';
$modulesourcefilter = 'f.module_source';
$amountfield = 'pf.amount';
@@ -441,11 +574,9 @@ if ($action == "create" || $action == "start" || $action == 'close') {
$resql = $db->query($sql);
if ($resql) {
$theoricalamountforterminal[$terminalid][$key] = $initialbalanceforterminal[$terminalid][$key];
$obj = $db->fetch_object($resql);
if ($obj) {
$theoricalamountforterminal[$terminalid][$key] = price2num($theoricalamountforterminal[$terminalid][$key] + $obj->total);
$theoricalamountforterminal[$terminalid][$key] = $obj->total;
$theoricalnbofinvoiceforterminal[$terminalid][$key] = $obj->nb;
}
} else {
@@ -659,7 +790,7 @@ if ($action == "create" || $action == "start") {
$object->fetch($id);
print $object->opening;
} else {
print(GETPOSTISSET('opening') ? price2num(GETPOST('opening', 'alpha')) : price($initialbalanceforterminal[$terminalid]['cash']));
print (GETPOSTISSET('opening') ? price2num(GETPOST('opening', 'alpha')) : price($initialbalanceforterminal[$terminalid]['cash']));
}
print '">';
print '</td>';
@@ -692,6 +823,7 @@ if ($action == "create" || $action == "start") {
print '</form>';
}
// View
if (empty($action) || $action == "view" || $action == "close") {
$result = $object->fetch($id);
@@ -732,22 +864,18 @@ if (empty($action) || $action == "view" || $action == "close") {
print $object->posnumber;
print "</td></tr>";
print '<tr><td class="titlefield nowrap">';
print $langs->trans("DateCreationShort");
print '</td><td colspan="3">';
print dol_print_date($object->date_creation, 'dayhour');
print '</td></tr>';
print '<tr><td class="nowrap">';
print $langs->trans("Period");
print '</td><td>';
print $object->year_close;
print($object->month_close ? "-" : "").sprintf("%02d", $object->month_close);
print($object->day_close ? "-" : "").sprintf("%02d", $object->day_close);
$dateend = dol_mktime($object->hour_close, $object->min_close, $object->sec_close, $object->month_close, $object->day_close, $object->year_close, 'gmt');
if (empty($object->day_close) && !empty($object->month_close)) {
$datestart = dol_get_last_day(($object->month_close == 1 ? $object->year_close - 1 : $object->year_close), ($object->month_close == 1 ? 12 : $object->month_close - 1), 'gmt');
} elseif (empty($object->day_close) && empty($object->month_close)) {
$datestart = dol_get_last_day($object->year_close - 1, $object->month_close, 'gmt');
} else {
$datestart = dol_time_plus_duree($dateend, -1, 'd', 0);
}
$datestart += 1; // Add 1 second
print($object->month_close ? "-".sprintf("%02d", $object->month_close) : "");
print($object->day_close ? "-".sprintf("%02d", $object->day_close) : "");
//print ' &nbsp; &nbsp; ';
$htmltooltip = '';
@@ -760,32 +888,60 @@ if (empty($action) || $action == "view" || $action == "close") {
print $form->textwithpicto('', $htmltooltip);
print '</td></tr>';
if ($object->lifetime_start) {
print '<tr><td class="titlefield nowrap">';
print $langs->trans("LifetimeAmount");
print '</td><td colspan="3">';
print '<span class="amount">'.price($object->card_lifetime + $object->cheque_lifetime + $object->cash_lifetime, 0, $langs, 1, -1, -1, $conf->currency).'</span>';
print ' &nbsp; <span class="opacitymedium">'.$langs->trans("since").' '.dol_print_date($object->lifetime_start, 'dayhour').' ('.$langs->trans("AllTerminals").')</span>';
print '</td></tr>';
}
print '</table>';
print '</div>';
print '<div class="fichehalfright">';
print '<div class="underbanner clearboth"></div>';
print '<table class="border tableforfield centpercent">';
print '<tr><td class="titlefield nowrap">';
print $langs->trans("DateCreationShort");
print '</td><td>';
print dol_print_date($object->date_creation, 'dayhour');
print '</td></tr>';
print '<tr><td valign="middle">'.$langs->trans("InitialBankBalance").' - '.$langs->trans("Cash").'</td><td>';
print '<span class="amount">'.price($object->opening, 0, $langs, 1, -1, -1, $conf->currency).'</span>';
print "</td></tr>";
print '<table class="noborder paymenttable centpercent">';
if ($object->status == $object::STATUS_CLOSED) {
print '<tr class="liste_titre"><td class="liste_titre">'.$langs->trans("Summary").'</td>';
print '<td class="liste_titre right">'.$langs->trans("InitialBankBalance").'</td>';
print '<td class="liste_titre right">'.$langs->trans("Sales").'</td>';
print '<td class="liste_titre right">'.$langs->trans("EndBankBalance").'</td>';
print "</tr>";
foreach ($arrayofpaymentmode as $key => $val) {
$realamountforpaymentmode = $object->$key;
print '<tr><td valign="middle">'.$langs->trans($val).'</td><td>';
if ($realamountforpaymentmode) {
print '<span class="amount">'.price($realamountforpaymentmode, 0, $langs, 1, -1, -1, $conf->currency).'</span>';
$newkey = $key.'_declared';
if (!property_exists($object, $key)) {
continue;
}
print "</td></tr>";
$realamountforpaymentmode = $object->$key;
$declaredamountforpaymentmode = $object->$newkey;
print '<tr>';
print '<td class="">'.$langs->trans($val).'</td>';
print '<td class="right">';
if ($key == 'cash') {
print '<span class="amount">'.price($object->opening, 0, $langs, 1, -1, -1, $conf->currency).'</span>';
}
print '</td>';
print '<td class="right">';
if ($realamountforpaymentmode) {
print '<span class="amount">'.($realamountforpaymentmode >= 0 ? '+' : '').price($realamountforpaymentmode, 0, $langs, 1, -1, -1, $conf->currency).'</span>';
}
print '</td>';
print '<td class="right">';
print '<span class="amount';
if ((($key == 'cash' ? $object->opening : 0) + $realamountforpaymentmode) != $declaredamountforpaymentmode) {
print ' error';
}
print '">';
print price($declaredamountforpaymentmode, 0, $langs, 1, -1, -1, $conf->currency).'</span>';
print '</td>';
print '</tr>';
}
}
@@ -840,6 +996,7 @@ if (empty($action) || $action == "view" || $action == "close") {
print '<br>';
print '<!-- section to enter declareda mount -->';
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';
@@ -875,7 +1032,7 @@ if (empty($action) || $action == "view" || $action == "close") {
print '<tr>';
// Initial amount
print '<td>'.$langs->trans("NbOfInvoices").'</td>';
print '<td>'.$langs->trans("NbOfPayments").'</td>';
print '<td class="center">';
print '</td>';
// Amount per payment type
@@ -896,25 +1053,30 @@ if (empty($action) || $action == "view" || $action == "close") {
print '<td class="center">';
print price($initialbalanceforterminal[$terminalid]['cash']).'<br>';
print '</td>';
// Amount per payment type
// Amount calculated per payment type
$i = 0;
foreach ($arrayofpaymentmode as $key => $val) {
print '<td align="center"'.($i == 0 ? ' class="hide0"' : '').'>';
print '<td class="smallheight center'.($i == 0 ? ' hide0' : '').'">';
if ($key == 'cash') {
$deltaforcash = ((float) $object->opening - $initialbalanceforterminal[$terminalid]['cash']);
print price($theoricalamountforterminal[$terminalid][$key] + $deltaforcash).'<br>';
print price($object->opening + $theoricalamountforterminal[$terminalid][$key]).'<br>';
print '<span class="opacitymedium small">('.price($object->opening).' + '.price($theoricalamountforterminal[$terminalid][$key]).')</span>';
} else {
print price($theoricalamountforterminal[$terminalid][$key]).'<br>';
}
print '<input type="hidden" name="'.$key.'_calculated" value="'.$theoricalamountforterminal[$terminalid][$key].'">';
print '</td>';
$i++;
}
// Save
print '<td align="center"></td>';
print '</tr>';
print '<tr>';
print '<td>'.$langs->trans("RealAmount").'</td>';
// Initial amount
print '<td class="center">';
print '<input ';
@@ -930,10 +1092,11 @@ if (empty($action) || $action == "view" || $action == "close") {
}
print '">';
print '</td>';
// Amount per payment type
$i = 0;
foreach ($arrayofpaymentmode as $key => $val) {
print '<td align="center"'.($i == 0 ? ' class="hide0"' : '').'>';
print '<td class="center '.($i == 0 ? ' hide0' : '').'">';
print '<input ';
if ($action == 'start') {
print 'disabled '; // To start cash user only can set opening cash
@@ -942,6 +1105,7 @@ if (empty($action) || $action == "view" || $action == "close") {
print '</td>';
$i++;
}
// Save
print '<td class="center">';
print '<input type="submit" name="cancel" class="button button-cancel" value="'.$langs->trans("Cancel").'">';

View File

@@ -82,6 +82,13 @@ class CashControl extends CommonObject
'cash' => array('type' => 'price', 'label' => 'Cash', 'enabled' => 1, 'visible' => 1, 'position' => 30, 'csslist' => 'amount'),
'cheque' => array('type' => 'price', 'label' => 'Cheque', 'enabled' => 1, 'visible' => 1, 'position' => 33, 'csslist' => 'amount'),
'card' => array('type' => 'price', 'label' => 'CreditCard', 'enabled' => 1, 'visible' => 1, 'position' => 36, 'csslist' => 'amount'),
'cash_declared' => array('type' => 'price', 'label' => 'CashDeclared', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'csslist' => 'amount'),
'cheque_declared' => array('type' => 'price', 'label' => 'ChequeDeclared', 'enabled' => 1, 'visible' => 1, 'position' => 41, 'csslist' => 'amount'),
'card_declared' => array('type' => 'price', 'label' => 'CreditCardDeclared', 'enabled' => 1, 'visible' => 1, 'position' => 42, 'csslist' => 'amount'),
'cash_lifetime' => array('type' => 'price', 'label' => 'CashLifetime', 'enabled' => 1, 'visible' => 0, 'position' => 45, 'csslist' => 'amount'),
'cheque_lifetime' => array('type' => 'price', 'label' => 'ChequeLifetime', 'enabled' => 1, 'visible' => 0, 'position' => 46, 'csslist' => 'amount'),
'card_lifetime' => array('type' => 'price', 'label' => 'CreditCardLifetime', 'enabled' => 1, 'visible' => 0, 'position' => 47, 'csslist' => 'amount'),
'lifetime_start' => array('type' => 'datetime', 'label' => 'LifetimeStartDate', 'enabled' => 1, 'visible' => 0, 'position' => 48, 'csslist' => 'center'),
'year_close' => array('type' => 'integer', 'label' => 'Year', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'position' => 50, 'css' => 'center'),
'month_close' => array('type' => 'integer', 'label' => 'Month', 'enabled' => 1, 'visible' => 1, 'position' => 55, 'css' => 'center'),
'day_close' => array('type' => 'integer', 'label' => 'Day', 'enabled' => 1, 'visible' => 1, 'position' => 60, 'css' => 'center'),
@@ -158,20 +165,55 @@ class CashControl extends CommonObject
public $posnumber;
/**
* @var float Cash amount
* @var float Cash amount earned during period (calculated)
*/
public $cash;
/**
* @var float cheque amount
* @var float Cheque amount earned during period (calculated)
*/
public $cheque;
/**
* @var float Card amountS
* @var float Card amount earned during period (calculated)
*/
public $card;
/**
* @var ?float Cash found/declared in account (is the
*/
public $cash_declared;
/**
* @var ?float Cheque found/declared in account
*/
public $cheque_declared;
/**
* @var ?float Card found/declared in account
*/
public $card_declared;
/**
* @var ?float Lifetime cash earned
*/
public $cash_lifetime;
/**
* @var ?float Lifetime cheque amount
*/
public $cheque_lifetime;
/**
* @var ?float Lifetime card amountS
*/
public $card_lifetime;
/**
* @var ?int Date when lifetime value start
*/
public $lifetime_start;
/**
* @var int User ID create
*/
@@ -301,7 +343,7 @@ class CashControl extends CommonObject
}
/**
* Validate cash fence
* Validate a cash register control
*
* @param User $user User
* @param int $notrigger No trigger
@@ -309,7 +351,6 @@ class CashControl extends CommonObject
*/
public function valid(User $user, $notrigger = 0)
{
global $conf, $langs;
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$error = 0;
@@ -324,7 +365,7 @@ class CashControl extends CommonObject
// Update request
$sql = "UPDATE ".MAIN_DB_PREFIX."pos_cash_fence";
$sql .= " SET status = ".self::STATUS_VALIDATED.",";
$sql .= " SET status = ".((int) self::STATUS_VALIDATED).",";
$sql .= " date_valid='".$this->db->idate($now)."',";
$sql .= " fk_user_valid = ".((int) $user->id);
$sql .= " WHERE rowid=".((int) $this->id);
@@ -368,6 +409,73 @@ class CashControl extends CommonObject
}
/**
* Close a cash register control
*
* @param User $user User
* @param int $notrigger No trigger
* @return int Return integer <0 if KO, >0 if OK
*/
public function close(User $user, $notrigger = 0)
{
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$error = 0;
// Protection
if ($this->status == self::STATUS_CLOSED) {
$this->error = get_class($this)."::valid action abandoned: already validated";
dol_syslog($this->error, LOG_WARNING);
return 0;
}
$now = dol_now();
// Update request
$sql = "UPDATE ".MAIN_DB_PREFIX."pos_cash_fence";
$sql .= " SET status = ".((int) self::STATUS_CLOSED).",";
$sql .= " date_valid = '".$this->db->idate($now)."',";
$sql .= " fk_user_valid = ".((int) $user->id);
$sql .= " WHERE rowid=".((int) $this->id);
$this->db->begin();
dol_syslog(get_class($this)."::close", LOG_DEBUG);
$resql = $this->db->query($sql);
if (!$resql) {
$error++;
$this->errors[] = "Error ".$this->db->lasterror();
}
if (!$error) {
$this->status = self::STATUS_CLOSED;
$this->date_valid = $now;
$this->fk_user_valid = $user->id;
}
if (!$error && !$notrigger) {
// Call trigger
$result = $this->call_trigger('CASHCONTROL_CLOSE', $user);
if ($result < 0) {
$error++;
}
// 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
*

View File

@@ -392,8 +392,8 @@ if ($resql) {
print '<div class="inline-block amount width100"></div>';
print '<div class="inline-block amount width100">'.price($cash).'</div>';
}
if (!$summaryonly && $object->status == $object::STATUS_VALIDATED && price2num($newcash) != price2num($object->cash)) {
print ' <div class="inline-block amountremaintopay fontsizeunset small"><> '.$langs->trans("Declared").': '.price($object->cash).'</div>';
if (!$summaryonly && $object->status == $object::STATUS_CLOSED && price2num($newcash) != price2num((float) $object->cash_declared)) {
print ' <div class="inline-block amountremaintopay fontsizeunset small"><> '.$langs->trans("Declared").': '.price($object->cash_declared).'</div>';
}
print "<br>";
@@ -401,8 +401,8 @@ if ($resql) {
print $langs->trans("PaymentTypeCHQ").(!empty($transactionspertype['CHQ']) ? ' ('.$transactionspertype['CHQ'].' '.$langs->trans("Articles").')' : '').' : ';
print '<div class="inline-block amount width100"></div>';
print '<div class="inline-block amount width100">'.price($cheque).'</div>';
if (!$summaryonly && $object->status == $object::STATUS_VALIDATED && price2num($cheque) != price2num($object->cheque)) {
print ' <div class="inline-block amountremaintopay fontsizeunset small"><> '.$langs->trans("Declared").' : '.price($object->cheque).'</div>';
if (!$summaryonly && $object->status == $object::STATUS_CLOSED && price2num($cheque) != price2num((float) $object->cheque_declared)) {
print ' <div class="inline-block amountremaintopay fontsizeunset small"><> '.$langs->trans("Declared").' : '.price($object->cheque_declared).'</div>';
}
print "<br>";
@@ -410,8 +410,8 @@ if ($resql) {
print $langs->trans("PaymentTypeCB").(!empty($transactionspertype['CB']) ? ' ('.$transactionspertype['CB'].' '.$langs->trans("Articles").')' : '').' : ';
print '<div class="inline-block amount width100"></div>';
print '<div class="inline-block amount width100">'.price($bank).'</div>';
if (!$summaryonly && $object->status == $object::STATUS_VALIDATED && price2num($bank) != price2num($object->card)) {
print ' <div class="inline-block amountremaintopay fontsizeunset small"><> '.$langs->trans("Declared").': '.price($object->card).'</div>';
if (!$summaryonly && $object->status == $object::STATUS_CLOSED && price2num($bank) != price2num((float) $object->card_declared)) {
print ' <div class="inline-block amountremaintopay fontsizeunset small"><> '.$langs->trans("Declared").': '.price($object->card_declared).'</div>';
}
print "<br>";

View File

@@ -107,7 +107,7 @@ class InterfaceActionsBlockedLog extends DolibarrTriggers
|| $action === 'BILL_SUPPLIER_VALIDATE' || (($action === 'BILL_SUPPLIER_DELETE' || $action === 'BILL_SUPPLIER_SENTBYMAIL') && ($object->statut != 0 || $object->status != 0))
|| $action === 'MEMBER_SUBSCRIPTION_CREATE' || $action === 'MEMBER_SUBSCRIPTION_MODIFY' || $action === 'MEMBER_SUBSCRIPTION_DELETE'
|| $action === 'DON_VALIDATE' || (($action === 'DON_MODIFY' || $action === 'DON_DELETE') && ($object->statut != 0 || $object->status != 0))
|| $action === 'CASHCONTROL_VALIDATE'
|| $action === 'CASHCONTROL_CLOSE'
|| (in_array($object->element, array('facture', 'supplier_invoice')) && $action === 'DOC_DOWNLOAD' && ($object->statut != 0 || $object->status != 0))
|| (in_array($object->element, array('facture', 'supplier_invoice')) && $action === 'DOC_PREVIEW' && ($object->statut != 0 || $object->status != 0))
|| (getDolGlobalString('BLOCKEDLOG_ADD_ACTIONS_SUPPORTED') && in_array($action, explode(',', getDolGlobalString('BLOCKEDLOG_ADD_ACTIONS_SUPPORTED'))))
@@ -119,7 +119,7 @@ class InterfaceActionsBlockedLog extends DolibarrTriggers
'DON_VALIDATE', 'DON_MODIFY', 'DON_DELETE'))) {
/** @var Don|Subscription $object */
$amounts = (float) $object->amount;
} elseif ($action == 'CASHCONTROL_VALIDATE') {
} elseif ($action == 'CASHCONTROL_CLOSE') {
/** @var CashControl $object */
$amounts = (float) $object->cash + (float) $object->cheque + (float) $object->card;
} else {

View File

@@ -404,12 +404,14 @@ ALTER TABLE llx_pos_cash_fence ADD COLUMN hour_close INTEGER DEFAULT null after
ALTER TABLE llx_pos_cash_fence ADD COLUMN min_close INTEGER DEFAULT null after hour_close;
ALTER TABLE llx_pos_cash_fence ADD COLUMN sec_close INTEGER DEFAULT null after min_close;
ALTER TABLE llx_pos_cash_fence ADD COLUMN cash_calculated double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN card_calculated double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN cheque_calculated double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN cash_declared double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN card_declared double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN cheque_declared double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN cash_lifetime double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN card_lifetime double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN cheque_lifetime double(24,8) DEFAULT null;
ALTER TABLE llx_pos_cash_fence ADD COLUMN lifetime_start datetime DEFAULT NULL;
-- end of migration

View File

@@ -20,18 +20,19 @@ CREATE TABLE llx_pos_cash_fence(
ref VARCHAR(64),
label VARCHAR(255),
opening double(24,8) default 0,
-- amount calculated
cash_calculated double(24,8) default NULL,
card_calculated double(24,8) default NULL,
cheque_calculated double(24,8) default NULL,
-- amount declared
-- amount
cash double(24,8) default 0,
card double(24,8) default 0,
cheque double(24,8) default 0,
-- amount declared
cash_declared double(24,8) default NULL,
card_declared double(24,8) default NULL,
cheque_declared double(24,8) default NULL,
-- lifetime amount (not used)
cash_lifetime double(24,8) default NULL,
card_lifetime double(24,8) default NULL,
cheque_lifetime double(24,8) default NULL,
lifetime_start datetime default NULL,
status INTEGER,
date_creation DATETIME NOT NULL,
date_valid DATETIME,

View File

@@ -706,3 +706,4 @@ TypeAmountOfEachNewDiscountSplit=Input amount for each part
SplitDiscountTitle=Split discount into several parts
DisputeStatus=Dispute status
DisputeOpen=Dispute open
Sales=Sales

View File

@@ -63,7 +63,8 @@ logBILL_PAYED=Customer invoice paid
logBILL_SENTBYMAIL=Customer invoice send by mail
logBILL_UNPAYED=Customer invoice set unpaid
logBILL_VALIDATE=Customer invoice validated
logCASHCONTROL_VALIDATE=Cash register closing
logCASHCONTROL_VALIDATE=Cash register validation
logCASHCONTROL_CLOSE=Cash register closing
logDOC_DOWNLOAD=Download of a validated document in order to print or send
logDOC_PREVIEW=Preview of a validated document in order to print or download
logDONATION_PAYMENT_CREATE=Donation payment created

View File

@@ -180,3 +180,5 @@ CashControlEndDateMustBeBeforeNow=It is not possible to make a monthly or yearly
CashControlEndDayMustNotBeInPast=It is not possible to make a daily cash control on a day in the future.
TheoricalView=Theorical view
SentOrderToKitchen=Send order to kitchen
LifetimeAmount=Lifetime amount
AllTerminals=All terminals

View File

@@ -1408,3 +1408,4 @@ KO=KO
AddThisPageToBookmarks = Add current page to bookmarks
EditBookmarks = List/edit bookmarks
Why=Why
since=since