Merge remote-tracking branch 'origin/3.8' into develop

Conflicts:
	htdocs/core/lib/admin.lib.php
	htdocs/core/modules/DolibarrModules.class.php
	htdocs/fourn/ajax/getSupplierPrices.php
	htdocs/fourn/class/fournisseur.commande.class.php
	htdocs/install/filelist.xml
	htdocs/install/mysql/migration/3.8.0-3.9.0.sql
	htdocs/societe/price.php
This commit is contained in:
Laurent Destailleur 2015-11-05 01:50:30 +01:00
commit e26940ddf7
13 changed files with 103 additions and 13441 deletions

File diff suppressed because one or more lines are too long

View File

@ -800,13 +800,14 @@ function unActivateModule($value, $requiredby=1)
} }
else else
{ {
// TODO Replace this after DolibarrModules is moved as abstract class with a try catch to show module is bugged //print $dir.$modFile;
// TODO Replace this after DolibarrModules is moved as abstract class with a try catch to show module we try to disable has not been found or could not be loaded
$genericMod = new DolibarrModules($db); $genericMod = new DolibarrModules($db);
$genericMod->name=preg_replace('/^mod/i','',$modName); $genericMod->name=preg_replace('/^mod/i','',$modName);
$genericMod->rights_class=strtolower(preg_replace('/^mod/i','',$modName)); $genericMod->rights_class=strtolower(preg_replace('/^mod/i','',$modName));
$genericMod->const_name='MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i','',$modName)); $genericMod->const_name='MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i','',$modName));
dol_syslog("modules::unActivateModule Failed to find module file, we use generic function with name " . $modName); dol_syslog("modules::unActivateModule Failed to find module file, we use generic function with name " . $modName);
$genericMod->_remove(''); $genericMod->_remove(array());
} }
// Desactivation des modules qui dependent de lui // Desactivation des modules qui dependent de lui

View File

@ -171,6 +171,7 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLengt
} }
}); });
} }
console.log("ajax_autocompleter new value selected, we trigger change");
$("#search_'.$htmlname.'").trigger("change"); // To tell that input text field was modified $("#search_'.$htmlname.'").trigger("change"); // To tell that input text field was modified
} }
,delay: 500 ,delay: 500

View File

@ -201,15 +201,15 @@ class DolibarrModules // Can not be abstract, because we need to insta
* *
* @param DoliDB $db Database handler * @param DoliDB $db Database handler
*/ */
public function __construct($db)
{
$this->db = $db;
}
// We should but can't set this as abstract because this will make dolibarr hang // We should but can't set this as abstract because this will make dolibarr hang
// after migration due to old module not implementing. We must wait PHP is able to make // after migration due to old module not implementing. We must wait PHP is able to make
// a try catch on Fatal error to manage this correctly. // a try catch on Fatal error to manage this correctly.
function __construct($db) // We need constructor into function unActivateModule into admin.lib.php
{
$this->db=$db;
}
/** /**
* Enables a module. * Enables a module.
* Inserts all informations into database * Inserts all informations into database

View File

@ -489,6 +489,7 @@ jQuery(document).ready(function() {
}); });
$("#prod_entry_mode_predef").on( "click", function() { $("#prod_entry_mode_predef").on( "click", function() {
console.log("click prod_entry_mode_predef");
setforpredef(); setforpredef();
jQuery('#trlinefordates').show(); jQuery('#trlinefordates').show();
}); });
@ -496,6 +497,7 @@ jQuery(document).ready(function() {
/* When changing predefined product, we reload list of supplier prices */ /* When changing predefined product, we reload list of supplier prices */
$("#idprod, #idprodfournprice").change(function() $("#idprod, #idprodfournprice").change(function()
{ {
console.log("change #idprod, #idprodfournprice, conf->global->MARGIN_TYPE=<?php echo $conf->global->MARGIN_TYPE ?>");
setforpredef(); setforpredef();
jQuery('#trlinefordates').show(); jQuery('#trlinefordates').show();
@ -510,12 +512,13 @@ jQuery(document).ready(function() {
$("#fournprice_predef").hide(); $("#fournprice_predef").hide();
$("#buying_price").val("").show(); $("#buying_price").val("").show();
/* Call post to load content of combo list fournprice_predef */ /* Call post to load content of combo list fournprice_predef */
$.post('<?php echo DOL_URL_ROOT; ?>/fourn/ajax/getSupplierPrices.php', { 'idprod': $(this).val() }, function(data) { $.post('<?php echo DOL_URL_ROOT; ?>/fourn/ajax/getSupplierPrices.php?bestpricefirst=1', { 'idprod': $(this).val() }, function(data) {
if (data && data.length > 0) if (data && data.length > 0)
{ {
var options = ''; var options = '';
var defaultkey = ''; var defaultkey = '';
var defaultprice = ''; var defaultprice = '';
var bestpricefound = 0;
var i = 0; var i = 0;
$(data).each(function() { $(data).each(function() {
if (this.id != 'pmpprice') if (this.id != 'pmpprice')
@ -524,24 +527,23 @@ jQuery(document).ready(function() {
this.price = parseFloat(this.price);//fix this.price >0 this.price = parseFloat(this.price);//fix this.price >0
// If margin is calculated on best supplier price, we set it by defaut (but only if value is not 0) // If margin is calculated on best supplier price, we set it by defaut (but only if value is not 0)
var defaultbuyprice = '<?php echo ((isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == '1')?'bestsupplierprice':''); ?>'; var defaultbuyprice = '<?php echo ((isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == '1')?'bestsupplierprice':''); ?>'; // We set here default value to use
if (i == 1 && this.price > 0 && 'bestsupplierprice' == defaultbuyprice) { defaultkey = this.id; defaultprice = this.price; } console.log(this.id+" "+this.price+" "+defaultbuyprice+" "+(this.price > 0));
if (bestpricefound == 0 && this.price > 0 && 'bestsupplierprice' == defaultbuyprice) { defaultkey = this.id; defaultprice = this.price; bestpricefound=1; } // bestpricefound is used to take the first price > 0
options += '<option value="'+this.id+'" price="'+this.price+'"';
if (this.price > 0 && i == 1) { defaultkey = this.id; defaultprice = this.price; }
options += '>'+this.label+'</option>';
} }
if (this.id == 'pmpprice') if (this.id == 'pmpprice')
{ {
// If margin is calculated on PMP, we set it by defaut (but only if value is not 0) // If margin is calculated on PMP, we set it by defaut (but only if value is not 0)
var defaultbuyprice = '<?php echo ((isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')?'pmp':''); ?>'; var defaultbuyprice = '<?php echo ((isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')?'pmp':''); ?>';
console.log(this.id+" "+this.price+" "+defaultbuyprice);
if (this.price > 0 && 'pmp' == defaultbuyprice) { defaultkey = this.id; defaultprice = this.price; } if (this.price > 0 && 'pmp' == defaultbuyprice) { defaultkey = this.id; defaultprice = this.price; }
options += '<option value="'+this.id+'" price="'+this.price+'">'+this.label+'</option>';
} }
options += '<option value="'+this.id+'" price="'+this.price+'">'+this.label+'</option>';
}); });
options += '<option value="inputprice" price="'+defaultprice+'"><?php echo $langs->trans("InputPrice"); ?></option>'; options += '<option value="inputprice" price="'+defaultprice+'"><?php echo $langs->trans("InputPrice"); ?></option>';
console.log("defaultkey="+defaultkey);
$("#fournprice_predef").html(options).show(); $("#fournprice_predef").html(options).show();
if (defaultkey != '') if (defaultkey != '')
{ {
@ -556,6 +558,7 @@ jQuery(document).ready(function() {
$("#buying_price").val(Math.round(defaultprice,<?php print ($conf->global->MAIN_MAX_DECIMALS_UNIT ? $conf->global->MAIN_MAX_DECIMALS_UNIT : 5); ?>)); $("#buying_price").val(Math.round(defaultprice,<?php print ($conf->global->MAIN_MAX_DECIMALS_UNIT ? $conf->global->MAIN_MAX_DECIMALS_UNIT : 5); ?>));
$("#fournprice_predef").change(function() { $("#fournprice_predef").change(function() {
console.log("change on fournprice_predef");
/* Hide field buying_price according to choice into list (if 'inputprice' or not) */ /* Hide field buying_price according to choice into list (if 'inputprice' or not) */
var linevalue=$(this).find('option:selected').val(); var linevalue=$(this).find('option:selected').val();
var pricevalue = $(this).find('option:selected').attr("price"); var pricevalue = $(this).find('option:selected').attr("price");

View File

@ -39,7 +39,7 @@ $langs->load('stocks');
/* /*
* View * View
*/ */
top_httphead(); top_httphead();
@ -49,7 +49,11 @@ if ($idprod > 0)
{ {
$producttmp=new ProductFournisseur($db); $producttmp=new ProductFournisseur($db);
$producttmp->fetch($idprod); $producttmp->fetch($idprod);
$productSupplierArray = $producttmp->list_product_fournisseur_price($idprod, 's.nom, pfp.quantity, pfp.price'); // We list all price per supplier, and then firstly with the lower quantity. So we can choose first one with enough quantity into list.
$sorttouse = 's.nom, pfp.quantity, pfp.price';
if (GETPOST('bestpricefirst')) $sorttouse = 'pfp.unitprice, s.nom, pfp.quantity, pfp.price';
$productSupplierArray = $producttmp->list_product_fournisseur_price($idprod, $sorttouse); // We list all price per supplier, and then firstly with the lower quantity. So we can choose first one with enough quantity into list.
if ( is_array($productSupplierArray)) if ( is_array($productSupplierArray))
{ {
foreach ($productSupplierArray as $productSupplier) foreach ($productSupplierArray as $productSupplier)
@ -81,13 +85,13 @@ if ($idprod > 0)
$label = price($price,0,$langs,0,0,-1,$conf->currency)."/".$langs->trans("Unit"); $label = price($price,0,$langs,0,0,-1,$conf->currency)."/".$langs->trans("Unit");
if ($productSupplier->fourn_ref) $label.=' ('.$productSupplier->fourn_ref.')'; if ($productSupplier->fourn_ref) $label.=' ('.$productSupplier->fourn_ref.')';
$prices[] = array("id" => $productSupplier->product_fourn_price_id, "price" => price($price,0,'',0), "label" => $label, "title" => $title); $prices[] = array("id" => $productSupplier->product_fourn_price_id, "price" => price2num($price,0,'',0), "label" => $label, "title" => $title); // For price field, we must use price2num(), for label or title, price()
} }
} }
// Add price for pmp // Add price for pmp
$price=$producttmp->pmp; $price=$producttmp->pmp;
$prices[] = array("id" => 'pmpprice', "price" => $price, "label" => $langs->trans("PMPValueShort").': '.price($price,0,$langs,0,0,-1,$conf->currency), "title" => $langs->trans("PMPValueShort").': '.price($price,0,$langs,0,0,-1,$conf->currency)); $prices[] = array("id" => 'pmpprice', "price" => price2num($price), "label" => $langs->trans("PMPValueShort").': '.price($price,0,$langs,0,0,-1,$conf->currency), "title" => $langs->trans("PMPValueShort").': '.price($price,0,$langs,0,0,-1,$conf->currency)); // For price field, we must use price2num(), for label or title, price()
} }
echo json_encode($prices); echo json_encode($prices);

View File

@ -2295,7 +2295,7 @@ class CommandeFournisseur extends CommonOrder
$clause = " WHERE"; $clause = " WHERE";
$sql = "SELECT c.rowid, c.date_creation as datec, c.fk_statut,c.date_livraison as delivery_date"; $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date";
$sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c"; $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
if (!$user->rights->societe->client->voir && !$user->societe_id) if (!$user->rights->societe->client->voir && !$user->societe_id)
{ {
@ -2315,7 +2315,7 @@ class CommandeFournisseur extends CommonOrder
$response = new WorkboardResponse(); $response = new WorkboardResponse();
$response->warning_delay=$conf->commande->fournisseur->warning_delay/60/60/24; $response->warning_delay=$conf->commande->fournisseur->warning_delay/60/60/24;
$response->label=$langs->trans("SuppliersOrdersToProcess"); $response->label=$langs->trans("SuppliersOrdersToProcess");
$response->url=DOL_URL_ROOT.'/fourn/commande/index.php'; $response->url=DOL_URL_ROOT.'/fourn/commande/list.php?statut=1,2,3';
$response->img=img_object($langs->trans("Orders"),"order"); $response->img=img_object($langs->trans("Orders"),"order");
while ($obj=$this->db->fetch_object($resql)) while ($obj=$this->db->fetch_object($resql))
@ -2323,7 +2323,7 @@ class CommandeFournisseur extends CommonOrder
$response->nbtodo++; $response->nbtodo++;
$commandestatic->date_livraison = $this->db->jdate($obj->delivery_date); $commandestatic->date_livraison = $this->db->jdate($obj->delivery_date);
$commandestatic->date_commande = $this->db->jdate($obj->datec); $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
$commandestatic->statut = $obj->fk_statut; $commandestatic->statut = $obj->fk_statut;
if ($commandestatic->hasDelay()) { if ($commandestatic->hasDelay()) {
@ -2477,11 +2477,11 @@ class CommandeFournisseur extends CommonOrder
public function hasDelay() public function hasDelay()
{ {
global $conf; global $conf;
$now = dol_now(); $now = dol_now();
$date_to_test = empty($this->date_livraison) ? $this->date_commande : $this->date_livraison; $date_to_test = empty($this->date_livraison) ? $this->date_commande : $this->date_livraison;
return ($this->statut != 3) && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay); return ($this->statut != 3) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
} }
} }

View File

@ -100,8 +100,14 @@ if ($socid > 0)
{ {
$fourn = new Fournisseur($db); $fourn = new Fournisseur($db);
$fourn->fetch($socid); $fourn->fetch($socid);
$title .= ' ('.$fourn->name.')'; $title .= ' - '.$fourn->name;
} }
if (GETPOST('statut','alpha'))
{
if (GETPOST('statut','alpha') == '1,2,3') $title.=' - '.$langs->trans("StatusOrderToProcessShort");
else $title.=' - '.$langs->trans($commandestatic->statuts[GETPOST('statut','alpha')]);
}
llxHeader('',$title); llxHeader('',$title);
@ -157,9 +163,9 @@ if ($sall)
if ($socid) $sql.= " AND s.rowid = ".$socid; if ($socid) $sql.= " AND s.rowid = ".$socid;
//Required triple check because statut=0 means draft filter //Required triple check because statut=0 means draft filter
if (GETPOST('statut', 'int') !== '') if (GETPOST('statut', 'alpha') !== '')
{ {
$sql .= " AND cf.fk_statut IN (".GETPOST('statut').")"; $sql .= " AND cf.fk_statut IN (".GETPOST('statut', 'alpha').")";
} }
if ($search_refsupp) if ($search_refsupp)
{ {
@ -245,7 +251,7 @@ if ($resql)
print '<td class="liste_titre">&nbsp;</td>'; print '<td class="liste_titre">&nbsp;</td>';
print '<td class="liste_titre">&nbsp;</td>'; print '<td class="liste_titre">&nbsp;</td>';
print '<td class="liste_titre" align="right">'; print '<td class="liste_titre" align="right">';
$formorder->selectSupplierOrderStatus($search_status,1,'search_status'); $formorder->selectSupplierOrderStatus((strstr($search_status, ',')?-1:$search_status),1,'search_status');
print '</td>'; print '</td>';
print '<td class="liste_titre" align="right"><input type="image" class="liste_titre" name="button_search" src="'.img_picto($langs->trans("Search"),'search.png','','',1).'" value="'.dol_escape_htmltag($langs->trans("Search")).'" title="'.dol_escape_htmltag($langs->trans("Search")).'">'; print '<td class="liste_titre" align="right"><input type="image" class="liste_titre" name="button_search" src="'.img_picto($langs->trans("Search"),'search.png','','',1).'" value="'.dol_escape_htmltag($langs->trans("Search")).'" title="'.dol_escape_htmltag($langs->trans("Search")).'">';
print '<input type="image" class="liste_titre" name="button_removefilter" src="'.img_picto($langs->trans("Search"),'searchclear.png','','',1).'" value="'.dol_escape_htmltag($langs->trans("RemoveFilter")).'" title="'.dol_escape_htmltag($langs->trans("RemoveFilter")).'">'; print '<input type="image" class="liste_titre" name="button_removefilter" src="'.img_picto($langs->trans("Search"),'searchclear.png','','',1).'" value="'.dol_escape_htmltag($langs->trans("RemoveFilter")).'" title="'.dol_escape_htmltag($langs->trans("RemoveFilter")).'">';

File diff suppressed because it is too large Load Diff

View File

@ -793,4 +793,9 @@ DELETE FROM llx_c_regions WHERE code_region=420 and fk_pays=4;
ALTER TABLE llx_c_paiement MODIFY COLUMN libelle varchar(62); ALTER TABLE llx_c_paiement MODIFY COLUMN libelle varchar(62);
ALTER TABLE llx_societe_remise_except MODIFY COLUMN description text NOT NULL; ALTER TABLE llx_societe_remise_except MODIFY COLUMN description text NOT NULL;
-- Fix bad data
update llx_opensurvey_sondage set format = 'D' where format = 'D+';
update llx_opensurvey_sondage set format = 'A' where format = 'A+';

View File

@ -18,6 +18,9 @@
-- -- VPGSQL8.2 DELETE FROM llx_usergroup_user WHERE fk_user NOT IN (SELECT rowid from llx_user); -- -- VPGSQL8.2 DELETE FROM llx_usergroup_user WHERE fk_user NOT IN (SELECT rowid from llx_user);
-- -- VMYSQL4.1 DELETE FROM llx_usergroup_user WHERE fk_usergroup NOT IN (SELECT rowid from llx_usergroup); -- -- VMYSQL4.1 DELETE FROM llx_usergroup_user WHERE fk_usergroup NOT IN (SELECT rowid from llx_usergroup);
-- Fix bad data
update llx_opensurvey_sondage set format = 'D' where format = 'D+';
update llx_opensurvey_sondage set format = 'A' where format = 'A+';
INSERT INTO llx_const (name, value, type, note, visible) values (__ENCRYPT('MAIN_DELAY_EXPENSEREPORTS_TO_PAY')__,__ENCRYPT('31')__,'chaine','Tolérance de retard avant alerte (en jours) sur les notes de frais impayées',0); INSERT INTO llx_const (name, value, type, note, visible) values (__ENCRYPT('MAIN_DELAY_EXPENSEREPORTS_TO_PAY')__,__ENCRYPT('31')__,'chaine','Tolérance de retard avant alerte (en jours) sur les notes de frais impayées',0);
INSERT INTO llx_const (name, value, type, note, visible) values ('MAIN_SIZE_SHORTLISTE_LIMIT','4','chaine','Longueur maximum des listes courtes (fiche client)',0); INSERT INTO llx_const (name, value, type, note, visible) values ('MAIN_SIZE_SHORTLISTE_LIMIT','4','chaine','Longueur maximum des listes courtes (fiche client)',0);
@ -174,6 +177,7 @@ CREATE TABLE IF NOT EXISTS llx_establishment (
) ENGINE=InnoDB; ) ENGINE=InnoDB;
ALTER TABLE llx_projet_task_time ADD COLUMN invoice_id integer DEFAULT NULL; ALTER TABLE llx_projet_task_time ADD COLUMN invoice_id integer DEFAULT NULL;
ALTER TABLE llx_projet_task_time ADD COLUMN invoice_line_id integer DEFAULT NULL; ALTER TABLE llx_projet_task_time ADD COLUMN invoice_line_id integer DEFAULT NULL;
@ -194,6 +198,7 @@ create table llx_stock_lotserial
) ENGINE=innodb; ) ENGINE=innodb;
-- Add budget tables
create table llx_budget create table llx_budget
( (
@ -235,11 +240,12 @@ ALTER TABLE llx_budget_lines ADD INDEX idx_budget_lines (fk_project);
ALTER TABLE llx_budget_lines ADD CONSTRAINT fk_budget_lines_budget FOREIGN KEY (fk_budget) REFERENCES llx_budget (rowid); ALTER TABLE llx_budget_lines ADD CONSTRAINT fk_budget_lines_budget FOREIGN KEY (fk_budget) REFERENCES llx_budget (rowid);
-- Add position field
ALTER TABLE llx_c_typent ADD COLUMN position integer NOT NULL DEFAULT 0; ALTER TABLE llx_c_typent ADD COLUMN position integer NOT NULL DEFAULT 0;
ALTER TABLE llx_c_forme_juridique ADD COLUMN position integer NOT NULL DEFAULT 0; ALTER TABLE llx_c_forme_juridique ADD COLUMN position integer NOT NULL DEFAULT 0;
ALTER TABLE llx_c_type_fees ADD COLUMN position integer NOT NULL DEFAULT 0; ALTER TABLE llx_c_type_fees ADD COLUMN position integer NOT NULL DEFAULT 0;
-- NEW Level multiprice generator based on per cent variations over base price -- NEW Level multiprice generator based on per cent variations over base price
CREATE TABLE llx_product_pricerules CREATE TABLE llx_product_pricerules
( (
@ -262,8 +268,6 @@ ALTER TABLE llx_opensurvey_sondage DROP COLUMN origin;
DROP TABLE llx_opensurvey_sujet_studs; DROP TABLE llx_opensurvey_sujet_studs;
CREATE TABLE llx_opensurvey_formquestions ( CREATE TABLE llx_opensurvey_formquestions (
rowid INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, rowid INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY,
id_sondage VARCHAR(16), id_sondage VARCHAR(16),

View File

@ -23,8 +23,8 @@ ChooseProduct/Service=Choose product or service
StartDate=Start date StartDate=Start date
EndDate=End date EndDate=End date
Launch=Start Launch=Start
ForceBuyingPriceIfNull=Force buying price if null ForceBuyingPriceIfNull=Force buying/cost price to selling price if not defined
ForceBuyingPriceIfNullDetails=If cost/buy price not defined, and this option "ON", margin will be zero on line (buying price = selling price), otherwise ("OFF"), marge will be equal to suggested default. ForceBuyingPriceIfNullDetails=If buying/cost price not defined, and this option "ON", margin will be zero on line (buying/cost price = selling price), otherwise ("OFF"), marge will be equal to suggested default.
MARGIN_METHODE_FOR_DISCOUNT=Margin method for global discounts MARGIN_METHODE_FOR_DISCOUNT=Margin method for global discounts
UseDiscountAsProduct=As a product UseDiscountAsProduct=As a product
UseDiscountAsService=As a service UseDiscountAsService=As a service

View File

@ -44,16 +44,16 @@ $langs->load("bills");
$action = GETPOST('action', 'alpha'); $action = GETPOST('action', 'alpha');
// Security check // Security check
$socid = GETPOST('socid', 'int'); $socid = GETPOST('socid', 'int')?GETPOST('socid', 'int'):GETPOST('id', 'int');
if ($user->societe_id) if ($user->societe_id)
$socid = $user->societe_id; $socid = $user->societe_id;
$result = restrictedArea($user, 'societe', $socid, '&societe'); $result = restrictedArea($user, 'societe', $socid, '&societe');
/**
* *************************************************** /*
* Price by customer * Actions
* ****************************************************
*/ */
if ($action == 'add_customer_price_confirm' && ! $_POST ["cancel"] && ($user->rights->produit->creer || $user->rights->service->creer)) { if ($action == 'add_customer_price_confirm' && ! $_POST ["cancel"] && ($user->rights->produit->creer || $user->rights->service->creer)) {
$update_child_soc = GETPOST('updatechildprice'); $update_child_soc = GETPOST('updatechildprice');
@ -114,6 +114,7 @@ if ($action == 'update_customer_price_confirm' && ! $_POST ["cancel"] && ($user-
$action = ''; $action = '';
} }
/* /*
* View * View
*/ */