Merge pull request #5533 from marcosgdf/prod-attr
Feature Request: NEW Added product attributes feature
This commit is contained in:
commit
b24f244f67
65
htdocs/attributes/admin/admin.php
Normal file
65
htdocs/attributes/admin/admin.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
|
||||
|
||||
$langs->load("admin");
|
||||
$langs->load("products");
|
||||
|
||||
// Security check
|
||||
if (! $user->admin || (empty($conf->product->enabled) && empty($conf->service->enabled)))
|
||||
accessforbidden();
|
||||
|
||||
if ($_POST) {
|
||||
|
||||
$value = GETPOST('PRODUIT_ATTRIBUTES_HIDECHILD');
|
||||
|
||||
if (dolibarr_set_const($db, 'PRODUIT_ATTRIBUTES_HIDECHILD', $value, 'chaine', 0, '', $conf->entity)) {
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
} else {
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$title = $langs->trans('ModuleSetup').' '.$langs->trans('ProductAttributes');
|
||||
llxHeader('', $title);
|
||||
|
||||
$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php">'.$langs->trans("BackToModuleList").'</a>';
|
||||
print load_fiche_titre($title,$linkback,'title_setup');
|
||||
|
||||
dol_fiche_head(array(), 'general', $tab, 0, 'product');
|
||||
|
||||
print '<form method="post">';
|
||||
print '<table class="noborder" width="100%">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<td>'.$langs->trans("Parameters").'</td>'."\n";
|
||||
print '<td align="right" width="60">'.$langs->trans("Value").'</td>'."\n";
|
||||
print '<td width="80"> </td></tr>'."\n";
|
||||
print '<tr><td>'.$langs->trans('HideProductCombinations').'</td><td>';
|
||||
print $form->selectyesno("PRODUIT_ATTRIBUTES_HIDECHILD",$conf->global->PRODUIT_ATTRIBUTES_HIDECHILD,1).'</td></tr>';
|
||||
print '</table>';
|
||||
print '<br><div style="text-align: center"><input type="submit" value="'.$langs->trans('Save').'" class="button"></div>';
|
||||
print '</form>';
|
||||
|
||||
llxFooter();
|
||||
|
||||
$db->close();
|
||||
|
||||
0
htdocs/attributes/admin/index.html
Normal file
0
htdocs/attributes/admin/index.html
Normal file
50
htdocs/attributes/ajax/getCombinations.php
Normal file
50
htdocs/attributes/ajax/getCombinations.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
define('NOTOKENRENEWAL','1');
|
||||
define('NOREQUIREMENU','1');
|
||||
define('NOREQUIREHTML','1');
|
||||
define('NOREQUIREAJAX','1');
|
||||
define('NOREQUIRESOC','1');
|
||||
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$id = GETPOST('id');
|
||||
|
||||
if (!$id) {
|
||||
print json_encode(array(
|
||||
'error' => 'ID not set'
|
||||
));
|
||||
die;
|
||||
}
|
||||
|
||||
$product = new Product($db);
|
||||
|
||||
if ($product->fetch($id) < 0) {
|
||||
print json_encode(array(
|
||||
'error' => 'Product not found'
|
||||
));
|
||||
}
|
||||
|
||||
$prodcomb = new ProductCombination($db);
|
||||
|
||||
echo json_encode($prodcomb->getUniqueAttributesAndValuesByFkProductParent($product->id));
|
||||
61
htdocs/attributes/ajax/get_attribute_values.php
Normal file
61
htdocs/attributes/ajax/get_attribute_values.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
define('NOTOKENRENEWAL','1');
|
||||
define('NOREQUIREMENU','1');
|
||||
define('NOREQUIREHTML','1');
|
||||
define('NOREQUIREAJAX','1');
|
||||
define('NOREQUIRESOC','1');
|
||||
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$id = GETPOST('id');
|
||||
|
||||
if (!$id) {
|
||||
print json_encode(array(
|
||||
'error' => 'ID not set'
|
||||
));
|
||||
die;
|
||||
}
|
||||
|
||||
$prodattr = new ProductAttribute($db);
|
||||
|
||||
if ($prodattr->fetch($id) < 0) {
|
||||
print json_encode(array(
|
||||
'error' => 'Attribute not found'
|
||||
));
|
||||
die;
|
||||
}
|
||||
|
||||
$prodattrval = new ProductAttributeValue($db);
|
||||
|
||||
$res = $prodattrval->fetchAllByProductAttribute($id);
|
||||
|
||||
if ($res == -1) {
|
||||
print json_encode(array(
|
||||
'error' => 'Internal error'
|
||||
));
|
||||
die;
|
||||
}
|
||||
|
||||
print json_encode($res);
|
||||
0
htdocs/attributes/ajax/index.html
Normal file
0
htdocs/attributes/ajax/index.html
Normal file
53
htdocs/attributes/ajax/orderAttribute.php
Normal file
53
htdocs/attributes/ajax/orderAttribute.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); // Disable token renewal
|
||||
if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU','1');
|
||||
if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML','1');
|
||||
if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1');
|
||||
if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1');
|
||||
if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1');
|
||||
if (! defined('NOREQUIREHOOK')) define('NOREQUIREHOOK','1'); // Disable "main.inc.php" hooks
|
||||
|
||||
require '../../main.inc.php';
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
top_httphead();
|
||||
|
||||
// Registering the location of boxes
|
||||
if (isset($_POST['roworder'])) {
|
||||
$roworder=GETPOST('roworder','alpha',2);
|
||||
|
||||
dol_syslog("AjaxOrderAttribute roworder=".$roworder, LOG_DEBUG);
|
||||
|
||||
$rowordertab = explode(',', $roworder);
|
||||
|
||||
foreach ($rowordertab as $value) {
|
||||
if (!empty($value)) {
|
||||
$newrowordertab[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
require DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
|
||||
|
||||
ProductAttribute::bulkUpdateOrder($db, $newrowordertab);
|
||||
}
|
||||
|
||||
245
htdocs/attributes/card.php
Normal file
245
htdocs/attributes/card.php
Normal file
@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
require '../main.inc.php';
|
||||
require 'class/ProductAttribute.class.php';
|
||||
require 'class/ProductAttributeValue.class.php';
|
||||
|
||||
$id = GETPOST('id');
|
||||
$valueid = GETPOST('valueid');
|
||||
$action = GETPOST('action');
|
||||
$label = GETPOST('label');
|
||||
$ref = GETPOST('ref');
|
||||
$confirm = GETPOST('confirm');
|
||||
|
||||
$prodattr = new ProductAttribute($db);
|
||||
$prodattrval = new ProductAttributeValue($db);
|
||||
|
||||
if ($prodattr->fetch($id) < 1) {
|
||||
dol_print_error($db, $langs->trans('ErrorRecordNotFound'));
|
||||
die;
|
||||
}
|
||||
|
||||
if ($_POST) {
|
||||
|
||||
if ($action == 'edit') {
|
||||
|
||||
$prodattr->label = $label;
|
||||
$prodattr->ref = $ref;
|
||||
|
||||
if ($prodattr->update() < 1) {
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
} else {
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
header('Location: '.dol_buildpath('/attributes/card.php?id='.$id, 2));
|
||||
die;
|
||||
}
|
||||
} elseif ($action == 'edit_value') {
|
||||
|
||||
if ($prodattrval->fetch($valueid) > 0) {
|
||||
|
||||
$prodattrval->ref = $ref;
|
||||
$prodattrval->value = GETPOST('value');
|
||||
|
||||
if ($prodattrval->update() > 0) {
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
} else {
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2));
|
||||
die;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($confirm == 'yes') {
|
||||
if ($action == 'confirm_delete') {
|
||||
|
||||
$db->begin();
|
||||
|
||||
$res = $prodattrval->deleteByFkAttribute($prodattr->id);
|
||||
|
||||
if ($res < 1 || ($prodattr->delete() < 1)) {
|
||||
$db->rollback();
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2));
|
||||
} else {
|
||||
$db->commit();
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
header('Location: '.dol_buildpath('/attributes/list.php', 2));
|
||||
}
|
||||
|
||||
die;
|
||||
} elseif ($action == 'confirm_deletevalue') {
|
||||
|
||||
if ($prodattrval->fetch($valueid) > 0) {
|
||||
|
||||
if ($prodattrval->delete() < 1) {
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
} else {
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
}
|
||||
|
||||
header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2));
|
||||
die;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$langs->load('products');
|
||||
|
||||
$title = $langs->trans('ProductAttributeName', dol_htmlentities($prodattr->label));
|
||||
$var = false;
|
||||
|
||||
llxHeader('', $title);
|
||||
|
||||
print_fiche_titre($title);
|
||||
|
||||
dol_fiche_head();
|
||||
|
||||
if ($action == 'edit') {
|
||||
print '<form method="post">';
|
||||
}
|
||||
|
||||
?>
|
||||
<table class="border" style="width: 100%">
|
||||
<tr>
|
||||
<td style="width: 15%" class="fieldrequired"><?php echo $langs->trans('Ref') ?></td>
|
||||
<td>
|
||||
<?php if ($action == 'edit') {
|
||||
print '<input type="text" name="ref" value="'.$prodattr->ref.'">';
|
||||
} else {
|
||||
print dol_htmlentities($prodattr->ref);
|
||||
} ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 15%" class="fieldrequired"><?php echo $langs->trans('Label') ?></td>
|
||||
<td>
|
||||
<?php if ($action == 'edit') {
|
||||
print '<input type="text" name="label" value="'.$prodattr->label.'">';
|
||||
} else {
|
||||
print dol_htmlentities($prodattr->label);
|
||||
} ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<?php
|
||||
dol_fiche_end();
|
||||
|
||||
if ($action == 'edit') { ?>
|
||||
<div style="text-align: center;">
|
||||
<div class="inline-block divButAction">
|
||||
<input type="submit" class="button" value="<?php echo $langs->trans('Save') ?>">
|
||||
<a href="card.php?id=<?php echo $prodattr->id ?>" class="butAction"><?php echo $langs->trans('Cancel') ?></a>
|
||||
</div>
|
||||
</div></form>
|
||||
<?php } else {
|
||||
|
||||
if ($action == 'delete') {
|
||||
$form = new Form($db);
|
||||
|
||||
print $form->formconfirm(
|
||||
"card.php?id=".$prodattr->id,
|
||||
$langs->trans('Delete'),
|
||||
$langs->trans('ProductAttributeDeleteDialog'),
|
||||
"confirm_delete",
|
||||
'',
|
||||
0,
|
||||
1
|
||||
);
|
||||
} elseif ($action == 'delete_value') {
|
||||
|
||||
if ($prodattrval->fetch($valueid) > 0) {
|
||||
|
||||
$form = new Form($db);
|
||||
|
||||
print $form->formconfirm(
|
||||
"card.php?id=".$prodattr->id."&valueid=".$prodattrval->id,
|
||||
$langs->trans('Delete'),
|
||||
$langs->trans('ProductAttributeValueDeleteDialog', dol_htmlentities($prodattrval->value), dol_htmlentities($prodattrval->ref)),
|
||||
"confirm_deletevalue",
|
||||
'',
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<div class="tabsAction">
|
||||
<div class="inline-block divButAction">
|
||||
<a href="card.php?id=<?php echo $prodattr->id ?>&action=edit" class="butAction"><?php echo $langs->trans('Modify') ?></a>
|
||||
<a href="card.php?id=<?php echo $prodattr->id ?>&action=delete" class="butAction"><?php echo $langs->trans('Delete') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($action == 'edit_value'): ?>
|
||||
<form method="post">
|
||||
<?php endif ?>
|
||||
|
||||
<table class="liste">
|
||||
<tr class="liste_titre">
|
||||
<th class="liste_titre"><?php echo $langs->trans('Ref') ?></th>
|
||||
<th class="liste_titre"><?php echo $langs->trans('Value') ?></th>
|
||||
<th class="liste_titre"></th>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($prodattrval->fetchAllByProductAttribute($prodattr->id) as $attrval): ?>
|
||||
<tr <?php echo $bc[!$var] ?>>
|
||||
<?php if ($action == 'edit_value' && ($valueid == $attrval->id)): ?>
|
||||
<td><input type="text" name="ref" value="<?php echo $attrval->ref ?>"></td>
|
||||
<td><input type="text" name="value" value="<?php echo $attrval->value ?>"></td>
|
||||
<td style="text-align: right">
|
||||
<input type="submit" value="<?php echo $langs->trans('Save') ?>" class="button">
|
||||
<a href="card.php?id=<?php echo $prodattr->id ?>" class="butAction"><?php echo $langs->trans('Cancel') ?></a>
|
||||
</td>
|
||||
<?php else: ?>
|
||||
<td><?php echo dol_htmlentities($attrval->ref) ?></td>
|
||||
<td><?php echo dol_htmlentities($attrval->value) ?></td>
|
||||
<td style="text-align: right">
|
||||
<a href="card.php?id=<?php echo $prodattr->id ?>&action=edit_value&valueid=<?php echo $attrval->id ?>"><?php echo img_edit() ?></a>
|
||||
<a href="card.php?id=<?php echo $prodattr->id ?>&action=delete_value&valueid=<?php echo $attrval->id ?>"><?php echo img_delete() ?></a>
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
<?php
|
||||
$var = !$var;
|
||||
endforeach
|
||||
?>
|
||||
</table>
|
||||
|
||||
<?php if ($action == 'edit_value'): ?>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="tabsAction">
|
||||
<div class="inline-block divButAction">
|
||||
<a href="create_val.php?id=<?php echo $prodattr->id ?>" class="butAction"><?php echo $langs->trans('Create') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
llxFooter();
|
||||
321
htdocs/attributes/class/ProductAttribute.class.php
Normal file
321
htdocs/attributes/class/ProductAttribute.class.php
Normal file
@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ProductAttribute
|
||||
* Used to represent a product attribute
|
||||
*/
|
||||
class ProductAttribute
|
||||
{
|
||||
/**
|
||||
* Database handler
|
||||
* @var DoliDB
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Id of the product attribute
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Ref of the product attribute
|
||||
* @var
|
||||
*/
|
||||
public $ref;
|
||||
|
||||
/**
|
||||
* Label of the product attribute
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* Order of attribute.
|
||||
* Lower ones will be shown first and higher ones last
|
||||
* @var int
|
||||
*/
|
||||
public $rang;
|
||||
|
||||
public function __construct(DoliDB $db)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
$this->db = $db;
|
||||
$this->entity = $conf->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the properties of a product attribute
|
||||
*
|
||||
* @param int $id Attribute id
|
||||
* @return int <1 KO, >1 OK
|
||||
*/
|
||||
public function fetch($id)
|
||||
{
|
||||
if (!$id) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
require_once __DIR__.'/../lib/product_attributes.lib.php';
|
||||
|
||||
$sql = "SELECT rowid, ref, label, rang FROM ".MAIN_DB_PREFIX."product_attribute WHERE rowid = ".(int) $id." AND entity IN (".getProductEntities($this->db).")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$this->db->num_rows($query)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$result = $this->db->fetch_object($query);
|
||||
|
||||
$this->id = $result->rowid;
|
||||
$this->ref = $result->ref;
|
||||
$this->label = $result->label;
|
||||
$this->rang = $result->rang;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all product attributes
|
||||
*
|
||||
* @return ProductAttribute[]
|
||||
*/
|
||||
public function fetchAll()
|
||||
{
|
||||
require_once __DIR__.'/../lib/product_attributes.lib.php';
|
||||
|
||||
$return = array();
|
||||
|
||||
$sql = 'SELECT rowid, ref, label, rang FROM '.MAIN_DB_PREFIX."product_attribute WHERE entity IN (".getProductEntities($this->db).')';
|
||||
$sql .= $this->db->order('rang', 'asc');
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
while ($result = $this->db->fetch_object($query)) {
|
||||
|
||||
$tmp = new ProductAttribute($this->db);
|
||||
$tmp->id = $result->rowid;
|
||||
$tmp->ref = $result->ref;
|
||||
$tmp->label = $result->label;
|
||||
$tmp->rang = $result->rang;
|
||||
|
||||
$return[] = $tmp;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a product attribute
|
||||
*
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//Ref must be uppercase
|
||||
$this->ref = strtoupper($this->ref);
|
||||
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute (ref, label, entity, rang)
|
||||
VALUES ('".$this->db->escape($this->ref)."', '".$this->db->escape($this->label)."', ".(int) $this->entity.", ".(int) $this->rang.")";
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if ($query) {
|
||||
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a product attribute
|
||||
*
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
//Ref must be uppercase
|
||||
$this->ref = strtoupper($this->ref);
|
||||
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."product_attribute SET ref = '".$this->db->escape($this->ref)."', label = '".$this->db->escape($this->label)."', rang = ".(int) $this->rang." WHERE rowid = ".(int) $this->id;
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a product attribute
|
||||
*
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute WHERE rowid = ".(int) $this->id;
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of products that are using this attribute
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countChildProducts()
|
||||
{
|
||||
require_once __DIR__.'/../lib/product_attributes.lib.php';
|
||||
|
||||
$sql = "SELECT COUNT(*) count FROM ".MAIN_DB_PREFIX."product_attribute_combination2val pac2v
|
||||
LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac2v.fk_prod_combination = pac.rowid WHERE pac2v.fk_prod_attr = ".(int) $this->id." AND pac.entity IN (".getProductEntities($this->db).")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
$result = $this->db->fetch_object($query);
|
||||
|
||||
return $result->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders the order of the attributes.
|
||||
* This is an internal function used by moveLine function
|
||||
*
|
||||
* @return int <0 KO >0 OK
|
||||
*/
|
||||
protected function reorderLines()
|
||||
{
|
||||
$tmp_order = array();
|
||||
|
||||
$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'product_attribute WHERE rang = 0';
|
||||
$sql .= $this->db->order('rang, rowid', 'asc');
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$query) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ($result = $this->db->fetch_object($query)) {
|
||||
$tmp_order[] = $result->rowid;
|
||||
}
|
||||
|
||||
foreach ($tmp_order as $order => $rowid) {
|
||||
$tmp = new ProductAttribute($this->db);
|
||||
$tmp->fetch($rowid);
|
||||
$tmp->rang = $order+1;
|
||||
|
||||
if ($tmp->update() < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to handle moveUp and moveDown functions
|
||||
*
|
||||
* @param string $type up/down
|
||||
* @return int <0 KO >0 OK
|
||||
*/
|
||||
private function moveLine($type)
|
||||
{
|
||||
if ($this->reorderLines() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->db->begin();
|
||||
|
||||
if ($type == 'up') {
|
||||
$newrang = $this->rang - 1;
|
||||
} else {
|
||||
$newrang = $this->rang + 1;
|
||||
}
|
||||
|
||||
$sql = 'UPDATE '.MAIN_DB_PREFIX.'product_attribute SET rang = '.$this->rang.' WHERE rang = '.$newrang;
|
||||
|
||||
if (!$this->db->query($sql)) {
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->rang = $newrang;
|
||||
|
||||
if ($this->update() < 0) {
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->db->commit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows this attribute before others
|
||||
*
|
||||
* @return int <0 KO >0 OK
|
||||
*/
|
||||
public function moveUp()
|
||||
{
|
||||
return $this->moveLine('up');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows this attribute after others
|
||||
*
|
||||
* @return int <0 KO >0 OK
|
||||
*/
|
||||
public function moveDown()
|
||||
{
|
||||
return $this->moveLine('down');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the order of all attributes. Used by AJAX page for drag&drop
|
||||
*
|
||||
* @param DoliDB $db Database handler
|
||||
* @param array $order Array with row id ordered in ascendent mode
|
||||
* @return int <0 KO >0 OK
|
||||
*/
|
||||
public static function bulkUpdateOrder(DoliDB $db, array $order)
|
||||
{
|
||||
$tmp = new ProductAttribute($db);
|
||||
|
||||
foreach ($order as $key => $attrid) {
|
||||
if ($tmp->fetch($attrid) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$tmp->rang = $key;
|
||||
|
||||
if ($tmp->update() < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
225
htdocs/attributes/class/ProductAttributeValue.class.php
Normal file
225
htdocs/attributes/class/ProductAttributeValue.class.php
Normal file
@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ProductAttributeValue
|
||||
* Used to represent a product attribute value
|
||||
*/
|
||||
class ProductAttributeValue
|
||||
{
|
||||
/**
|
||||
* Database handler
|
||||
* @var DoliDB
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Attribute value id
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Product attribute id
|
||||
* @var int
|
||||
*/
|
||||
public $fk_product_attribute;
|
||||
|
||||
/**
|
||||
* Attribute value ref
|
||||
* @var string
|
||||
*/
|
||||
public $ref;
|
||||
|
||||
/**
|
||||
* Attribute value value
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
public function __construct(DoliDB $db)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
$this->db = $db;
|
||||
$this->entity = $conf->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a product attribute value
|
||||
*
|
||||
* @param int $valueid Product attribute value id
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function fetch($valueid)
|
||||
{
|
||||
require_once __DIR__.'/../lib/product_attributes.lib.php';
|
||||
|
||||
$sql = "SELECT rowid, fk_product_attribute, ref, value FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE rowid = ".(int) $valueid." AND entity IN (".getProductEntities($this->db).")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$query) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!$this->db->num_rows($query)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$result = $this->db->fetch_object($query);
|
||||
|
||||
$this->id = $result->rowid;
|
||||
$this->fk_product_attribute = $result->fk_product_attribute;
|
||||
$this->ref = $result->ref;
|
||||
$this->value = $result->value;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all product attribute values of a product attribute
|
||||
*
|
||||
* @param int $prodattr_id Product attribute id
|
||||
* @param bool $only_used Fetch only used attribute values
|
||||
* @return ProductAttributeValue[]
|
||||
*/
|
||||
public function fetchAllByProductAttribute($prodattr_id, $only_used = false)
|
||||
{
|
||||
require_once __DIR__.'/../lib/product_attributes.lib.php';
|
||||
|
||||
$return = array();
|
||||
|
||||
$sql = 'SELECT ';
|
||||
|
||||
if ($only_used) {
|
||||
$sql .= 'DISTINCT ';
|
||||
}
|
||||
|
||||
$sql .= 'v.fk_product_attribute, v.rowid, v.ref, v.value FROM '.MAIN_DB_PREFIX.'product_attribute_value v ';
|
||||
|
||||
if ($only_used) {
|
||||
$sql .= 'LEFT JOIN '.MAIN_DB_PREFIX.'product_attribute_combination2val c2v ON c2v.fk_prod_attr_val = v.rowid ';
|
||||
$sql .= 'LEFT JOIN '.MAIN_DB_PREFIX.'product_attribute_combination c ON c.rowid = c2v.fk_prod_combination ';
|
||||
$sql .= 'LEFT JOIN '.MAIN_DB_PREFIX.'product p ON p.rowid = c.fk_product_child ';
|
||||
}
|
||||
|
||||
$sql .= 'WHERE v.fk_product_attribute = '.(int) $prodattr_id;
|
||||
|
||||
if ($only_used) {
|
||||
$sql .= ' AND c2v.rowid IS NOT NULL AND p.tosell = 1';
|
||||
}
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
while ($result = $this->db->fetch_object($query)) {
|
||||
|
||||
$tmp = new ProductAttributeValue($this->db);
|
||||
$tmp->fk_product_attribute = $result->fk_product_attribute;
|
||||
$tmp->id = $result->rowid;
|
||||
$tmp->ref = $result->ref;
|
||||
$tmp->value = $result->value;
|
||||
|
||||
$return[] = $tmp;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a value for a product attribute
|
||||
*
|
||||
* @return int <0 KO >0 OK
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
if (!$this->fk_product_attribute) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Ref must be uppercase
|
||||
$this->ref = strtoupper($this->ref);
|
||||
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_value (fk_product_attribute, ref, value, entity)
|
||||
VALUES ('".(int) $this->fk_product_attribute."', '".$this->db->escape($this->ref)."',
|
||||
'".$this->db->escape($this->value)."', ".(int) $this->entity.")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if ($query) {
|
||||
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_value');
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a product attribute value
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
//Ref must be uppercase
|
||||
$this->ref = strtoupper($this->ref);
|
||||
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."product_attribute_value
|
||||
SET fk_product_attribute = '".(int) $this->fk_product_attribute."', ref = '".$this->db->escape($this->ref)."',
|
||||
value = '".$this->db->escape($this->value)."' WHERE rowid = ".(int) $this->id;
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a product attribute value
|
||||
*
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE rowid = ".(int) $this->id;
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all product attribute values by a product attribute id
|
||||
*
|
||||
* @param int $fk_attribute Product attribute id
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function deleteByFkAttribute($fk_attribute)
|
||||
{
|
||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE fk_product_attribute = ".(int) $fk_attribute;
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
645
htdocs/attributes/class/ProductCombination.class.php
Normal file
645
htdocs/attributes/class/ProductCombination.class.php
Normal file
@ -0,0 +1,645 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ProductCombination
|
||||
* Used to represent a product combination
|
||||
*/
|
||||
class ProductCombination
|
||||
{
|
||||
/**
|
||||
* Database handler
|
||||
* @var DoliDB
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Rowid of combination
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Rowid of parent product
|
||||
* @var int
|
||||
*/
|
||||
public $fk_product_parent;
|
||||
|
||||
/**
|
||||
* Rowid of child product
|
||||
* @var int
|
||||
*/
|
||||
public $fk_product_child;
|
||||
|
||||
/**
|
||||
* Price variation
|
||||
* @var float
|
||||
*/
|
||||
public $variation_price;
|
||||
|
||||
/**
|
||||
* Is the price variation a relative variation?
|
||||
* @var bool
|
||||
*/
|
||||
public $variation_price_percentage = false;
|
||||
|
||||
/**
|
||||
* Weight variation
|
||||
* @var float
|
||||
*/
|
||||
public $variation_weight;
|
||||
|
||||
/**
|
||||
* Combination entity
|
||||
* @var int
|
||||
*/
|
||||
public $entity;
|
||||
|
||||
public function __construct(DoliDB $db)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
$this->db = $db;
|
||||
$this->entity = $conf->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a combination by its rowid
|
||||
*
|
||||
* @param int $rowid Row id
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function fetch($rowid)
|
||||
{
|
||||
require_once __DIR__.'/../lib/product_attributes.lib.php';
|
||||
|
||||
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE rowid = ".(int) $rowid." AND entity IN (".getProductEntities($this->db).")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$query) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!$this->db->num_rows($query)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$result = $this->db->fetch_object($query);
|
||||
|
||||
$this->id = $result->rowid;
|
||||
$this->fk_product_parent = $result->fk_product_parent;
|
||||
$this->fk_product_child = $result->fk_product_child;
|
||||
$this->variation_price = $result->variation_price;
|
||||
$this->variation_price_percentage = $result->variation_price_percentage;
|
||||
$this->variation_weight = $result->variation_weight;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a product combination by a child product row id
|
||||
*
|
||||
* @param int $fk_child Product row id
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function fetchByFkProductChild($fk_child)
|
||||
{
|
||||
require_once __DIR__.'/../lib/product_attributes.lib.php';
|
||||
|
||||
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_child = ".(int) $fk_child." AND entity IN (".getProductEntities($this->db).")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$query) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!$this->db->num_rows($query)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$result = $this->db->fetch_object($query);
|
||||
|
||||
$this->id = $result->rowid;
|
||||
$this->fk_product_parent = $result->fk_product_parent;
|
||||
$this->fk_product_child = $result->fk_product_child;
|
||||
$this->variation_price = $result->variation_price;
|
||||
$this->variation_price_percentage = $result->variation_price_percentage;
|
||||
$this->variation_weight = $result->variation_weight;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all product combinations by the product parent row id
|
||||
*
|
||||
* @param int $fk_product_parent Rowid of parent product
|
||||
* @return int|ProductCombination[] <0 KO
|
||||
*/
|
||||
public function fetchAllByFkProductParent($fk_product_parent)
|
||||
{
|
||||
require_once __DIR__.'/../lib/product_attributes.lib.php';
|
||||
|
||||
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_parent = ".(int) $fk_product_parent." AND entity IN (".getProductEntities($this->db).")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$query) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$return = array();
|
||||
|
||||
while ($result = $this->db->fetch_object($query)) {
|
||||
|
||||
$tmp = new ProductCombination($this->db);
|
||||
$tmp->id = $result->rowid;
|
||||
$tmp->fk_product_parent = $result->fk_product_parent;
|
||||
$tmp->fk_product_child = $result->fk_product_child;
|
||||
$tmp->variation_price = $result->variation_price;
|
||||
$tmp->variation_price_percentage = $result->variation_price_percentage;
|
||||
$tmp->variation_weight = $result->variation_weight;
|
||||
|
||||
$return[] = $tmp;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a product attribute combination
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_combination
|
||||
(fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, entity)
|
||||
VALUES (".(int) $this->fk_product_parent.", ".(int) $this->fk_product_child.",
|
||||
".(float) $this->variation_price.", ".(int) $this->variation_price_percentage.",
|
||||
".(float) $this->variation_weight.", ".(int) $this->entity.")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$query) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_combination');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a product combination
|
||||
*
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."product_attribute_combination
|
||||
SET fk_product_parent = ".(int) $this->fk_product_parent.", fk_product_child = ".(int) $this->fk_product_child.",
|
||||
variation_price = ".(float) $this->variation_price.", variation_price_percentage = ".(int) $this->variation_price_percentage.",
|
||||
variation_weight = ".(float) $this->variation_weight." WHERE rowid = ".(int) $this->id;
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$query) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$parent = new Product($this->db);
|
||||
$parent->fetch($this->fk_product_parent);
|
||||
|
||||
$this->updateProperties($parent);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a product combination
|
||||
*
|
||||
* @return int <0 KO >0 OK
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->db->begin();
|
||||
|
||||
$comb2val = new ProductCombination2ValuePair($this->db);
|
||||
$comb2val->deleteByFkCombination($this->id);
|
||||
|
||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE rowid = ".(int) $this->id;
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
$this->db->commit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all product combinations of a parent product
|
||||
*
|
||||
* @param int $fk_product_parent Rowid of parent product
|
||||
* @return int <0 KO >0 OK
|
||||
*/
|
||||
public function deleteByFkProductParent($fk_product_parent)
|
||||
{
|
||||
$this->db->begin();
|
||||
|
||||
foreach ($this->fetchAllByFkProductParent($fk_product_parent) as $prodcomb) {
|
||||
|
||||
$prodstatic = new Product($this->db);
|
||||
|
||||
$res = $prodstatic->fetch($prodcomb->fk_product_child);
|
||||
|
||||
if ($res > 0) {
|
||||
$res = $prodcomb->delete();
|
||||
}
|
||||
|
||||
if ($res > 0 && !$prodstatic->isObjectUsed($prodstatic->id)) {
|
||||
$res = $prodstatic->delete();
|
||||
}
|
||||
|
||||
if ($res < 0) {
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->commit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the weight of the child product. The price must be updated using Product::updatePrices
|
||||
*
|
||||
* @param Product $parent Parent product
|
||||
* @return int >0 OK <0 KO
|
||||
*/
|
||||
public function updateProperties(Product $parent)
|
||||
{
|
||||
global $user, $conf;
|
||||
|
||||
$this->db->begin();
|
||||
|
||||
$child = new Product($this->db);
|
||||
$child->fetch($this->fk_product_child);
|
||||
$child->price_autogen = $parent->price_autogen;
|
||||
$child->weight = $parent->weight + $this->variation_weight;
|
||||
$child->weight_units = $parent->weight_units;
|
||||
|
||||
if ($child->update($child->id, $user) > 0) {
|
||||
|
||||
$new_vat = $parent->tva_tx;
|
||||
$new_npr = $parent->tva_npr;
|
||||
|
||||
// MultiPrix
|
||||
if (! empty($conf->global->PRODUIT_MULTIPRICES)) {
|
||||
$new_type = $parent->multiprices_base_type[1];
|
||||
$new_min_price = $parent->multiprices_min[1];
|
||||
$new_psq = $parent->multiprices_recuperableonly[1];
|
||||
|
||||
if ($new_type == 'TTC') {
|
||||
$new_price = $parent->multiprices_ttc[1];
|
||||
} else {
|
||||
$new_price = $parent->multiprices[1];
|
||||
}
|
||||
} else {
|
||||
$new_type = $parent->price_base_type;
|
||||
$new_min_price = $parent->price_min;
|
||||
$new_psq = $parent->price_by_qty;
|
||||
|
||||
if ($new_type == 'TTC') {
|
||||
$new_price = $parent->price_ttc;
|
||||
} else {
|
||||
$new_price = $parent->price;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->variation_price_percentage) {
|
||||
$new_price *= 1 + ($this->variation_price/100);
|
||||
} else {
|
||||
$new_price += $this->variation_price;
|
||||
}
|
||||
|
||||
$child->updatePrice($new_price, $new_type, $user, $new_vat, $new_min_price, 1, $new_npr, $new_psq);
|
||||
|
||||
$this->db->commit();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the combination that matches the given features.
|
||||
*
|
||||
* @param int $prodid Id of parent product
|
||||
* @param array $features Format: [$attr] => $attr_val
|
||||
* @return false|ProductCombination false if not found
|
||||
*/
|
||||
public function fetchByProductCombination2ValuePairs($prodid, array $features)
|
||||
{
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php';
|
||||
|
||||
$actual_comp = array();
|
||||
|
||||
$prodcomb2val = new ProductCombination2ValuePair($this->db);
|
||||
$prodcomb = new ProductCombination($this->db);
|
||||
|
||||
foreach ($features as $attr => $attr_val) {
|
||||
$actual_comp[$attr] = $attr_val;
|
||||
}
|
||||
|
||||
foreach ($prodcomb->fetchAllByFkProductParent($prodid) as $prc) {
|
||||
|
||||
$values = array();
|
||||
|
||||
foreach ($prodcomb2val->fetchByFkCombination($prc->id) as $value) {
|
||||
$values[$value->fk_prod_attr] = $value->fk_prod_attr_val;
|
||||
}
|
||||
|
||||
$check1 = count(array_diff_assoc($values, $actual_comp));
|
||||
$check2 = count(array_diff_assoc($actual_comp, $values));
|
||||
|
||||
if (!$check1 && !$check2) {
|
||||
return $prc;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all unique attributres for a parent product
|
||||
*
|
||||
* @param int $productid Product rowid
|
||||
* @return ProductAttribute[]
|
||||
*/
|
||||
public function getUniqueAttributesAndValuesByFkProductParent($productid)
|
||||
{
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php';
|
||||
|
||||
$attributes = array();
|
||||
|
||||
//Attributes
|
||||
$sql = "SELECT DISTINCT fk_prod_attr, a.rang
|
||||
FROM ".MAIN_DB_PREFIX."product_attribute_combination2val c2v LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination c
|
||||
ON c2v.fk_prod_combination = c.rowid
|
||||
LEFT JOIN ".MAIN_DB_PREFIX."product p ON p.rowid = c.fk_product_child
|
||||
LEFT JOIN ".MAIN_DB_PREFIX."product_attribute a ON a.rowid = fk_prod_attr
|
||||
WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
|
||||
|
||||
$sql .= $this->db->order('a.rang', 'asc');
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
//Values
|
||||
while ($result = $this->db->fetch_object($query)) {
|
||||
$attr = new ProductAttribute($this->db);
|
||||
$attr->fetch($result->fk_prod_attr);
|
||||
|
||||
$tmp = new stdClass();
|
||||
$tmp->id = $attr->id;
|
||||
$tmp->ref = $attr->ref;
|
||||
$tmp->label = $attr->label;
|
||||
$tmp->values = array();
|
||||
|
||||
$attrval = new ProductAttributeValue($this->db);
|
||||
foreach ($res = $attrval->fetchAllByProductAttribute($attr->id, true) as $val) {
|
||||
$tmp->values[] = $val;
|
||||
}
|
||||
|
||||
$attributes[] = $tmp;
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a product combination. Check usages to find more about its use
|
||||
*
|
||||
* Format of $combinations array:
|
||||
* array(
|
||||
* 0 => array(
|
||||
* attr => value,
|
||||
* attr2 => value
|
||||
* [...]
|
||||
* ),
|
||||
* [...]
|
||||
* )
|
||||
*
|
||||
* @param Product $product Parent product
|
||||
* @param array $combinations Attribute and value combinations.
|
||||
* @param array $variations Price and weight variations
|
||||
* @param bool $price_var_percent Is the price variation a relative variation?
|
||||
* @param bool|float $forced_pricevar If the price variation is forced
|
||||
* @param bool|float $forced_weightvar If the weight variation is forced
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public static function createProductCombination(Product $product, array $combinations, array $variations, $price_var_percent = false, $forced_pricevar = false, $forced_weightvar = false)
|
||||
{
|
||||
global $db, $user;
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php';
|
||||
|
||||
$db->begin();
|
||||
|
||||
$newproduct = clone $product;
|
||||
|
||||
//Final weight impact
|
||||
$weight_impact = $forced_weightvar;
|
||||
|
||||
if ($forced_weightvar === false) {
|
||||
$weight_impact = 0;
|
||||
}
|
||||
|
||||
//Final price impact
|
||||
$price_impact = $forced_pricevar;
|
||||
|
||||
if ($forced_pricevar === false) {
|
||||
$price_impact = 0;
|
||||
}
|
||||
|
||||
$newcomb = new ProductCombination($db);
|
||||
$existingCombination = $newcomb->fetchByProductCombination2ValuePairs($product->id, $combinations);
|
||||
|
||||
if ($existingCombination) {
|
||||
$newcomb = $existingCombination;
|
||||
} else {
|
||||
$newcomb->fk_product_parent = $product->id;
|
||||
|
||||
if ($newcomb->create() < 0) {
|
||||
$db->rollback();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
$prodattr = new ProductAttribute($db);
|
||||
$prodattrval = new ProductAttributeValue($db);
|
||||
|
||||
foreach ($combinations as $currcombattr => $currcombval) {
|
||||
|
||||
//This was checked earlier, so no need to double check
|
||||
$prodattr->fetch($currcombattr);
|
||||
$prodattrval->fetch($currcombval);
|
||||
|
||||
//If there is an existing combination, there is no need to duplicate the valuepair
|
||||
if (!$existingCombination) {
|
||||
$tmp = new ProductCombination2ValuePair($db);
|
||||
$tmp->fk_prod_attr = $currcombattr;
|
||||
$tmp->fk_prod_attr_val = $currcombval;
|
||||
$tmp->fk_prod_combination = $newcomb->id;
|
||||
|
||||
if ($tmp->create() < 0) {
|
||||
$db->rollback();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($forced_weightvar === false) {
|
||||
$weight_impact += (float) price2num($variations[$currcombattr][$currcombval]['weight']);
|
||||
}
|
||||
if ($forced_pricevar === false) {
|
||||
$price_impact += (float) price2num($variations[$currcombattr][$currcombval]['price']);
|
||||
}
|
||||
|
||||
$newproduct->ref .= '_'.$prodattrval->ref;
|
||||
|
||||
//The first one should not contain a linebreak
|
||||
if ($newproduct->description) {
|
||||
$newproduct->description .= '<br>';
|
||||
}
|
||||
$newproduct->description .= '<strong>'.$prodattr->label.':</strong> '.$prodattrval->value;
|
||||
}
|
||||
|
||||
$newcomb->variation_price_percentage = $price_var_percent;
|
||||
$newcomb->variation_price = $price_impact;
|
||||
$newcomb->variation_weight = $weight_impact;
|
||||
|
||||
$newproduct->weight += $weight_impact;
|
||||
|
||||
//To avoid wrong information in price history log
|
||||
$newproduct->price = 0;
|
||||
$newproduct->price_ttc = 0;
|
||||
$newproduct->price_min = 0;
|
||||
$newproduct->price_min_ttc = 0;
|
||||
|
||||
if ($newproduct->create($user) < 0) {
|
||||
|
||||
//In case the error is not related with an already existing product
|
||||
if ($newproduct->error != 'ErrorProductAlreadyExists') {
|
||||
$db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is an existing combination, then we update the prices and weight
|
||||
* Otherwise, we try adding a random number to the ref
|
||||
*/
|
||||
|
||||
if ($newcomb->fk_product_child) {
|
||||
$res = $newproduct->fetch($existingCombination->fk_product_child);
|
||||
} else {
|
||||
$orig_prod_ref = $newproduct->ref;
|
||||
$i = 1;
|
||||
|
||||
do {
|
||||
$newproduct->ref = $orig_prod_ref.$i;
|
||||
$res = $newproduct->create($user);
|
||||
|
||||
if ($newproduct->error != 'ErrorProductAlreadyExists') {
|
||||
break;
|
||||
}
|
||||
|
||||
$i++;
|
||||
} while ($res < 0);
|
||||
}
|
||||
|
||||
if ($res < 0) {
|
||||
$db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
$newproduct->weight += $weight_impact;
|
||||
}
|
||||
|
||||
$newcomb->fk_product_child = $newproduct->id;
|
||||
|
||||
if ($newcomb->update() < 0) {
|
||||
$db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all product combinations from the origin product to the destination product
|
||||
*
|
||||
* @param int $origProductId Origin product id
|
||||
* @param Product $destProduct Destination product
|
||||
* @return int >0 OK <0 KO
|
||||
*/
|
||||
public function copyAll($origProductId, Product $destProduct)
|
||||
{
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php';
|
||||
|
||||
//To prevent a loop
|
||||
if ($origProductId == $destProduct->id) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$prodcomb = new ProductCombination($this->db);
|
||||
$prodcomb2val = new ProductCombination2ValuePair($this->db);
|
||||
|
||||
//Retrieve all product combinations
|
||||
$combinations = $prodcomb->fetchAllByFkProductParent($origProductId);
|
||||
|
||||
foreach ($combinations as $combination) {
|
||||
|
||||
$variations = array();
|
||||
|
||||
foreach ($prodcomb2val->fetchByFkCombination($combination->id) as $tmp_pc2v) {
|
||||
$variations[$tmp_pc2v->fk_prod_attr] = $tmp_pc2v->fk_prod_attr_val;
|
||||
}
|
||||
|
||||
if (self::createProductCombination(
|
||||
$destProduct,
|
||||
$variations,
|
||||
array(),
|
||||
$combination->variation_price_percentage,
|
||||
$combination->variation_price,
|
||||
$combination->variation_weight
|
||||
) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
155
htdocs/attributes/class/ProductCombination2ValuePair.class.php
Normal file
155
htdocs/attributes/class/ProductCombination2ValuePair.class.php
Normal file
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ProductCombination2ValuePair
|
||||
* Used to represent the relation between a product combination, a product attribute and a product attribute value
|
||||
*/
|
||||
class ProductCombination2ValuePair
|
||||
{
|
||||
/**
|
||||
* Database handler
|
||||
* @var DoliDB
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Combination 2 value pair id
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Product combination id
|
||||
* @var int
|
||||
*/
|
||||
public $fk_prod_combination;
|
||||
|
||||
/**
|
||||
* Product attribute id
|
||||
* @var int
|
||||
*/
|
||||
public $fk_prod_attr;
|
||||
|
||||
/**
|
||||
* Product attribute value id
|
||||
* @var int
|
||||
*/
|
||||
public $fk_prod_attr_val;
|
||||
|
||||
public function __construct(DoliDB $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates this class to a human-readable string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
|
||||
|
||||
$prodattr = new ProductAttribute($this->db);
|
||||
$prodattrval = new ProductAttributeValue($this->db);
|
||||
|
||||
$prodattr->fetch($this->fk_prod_attr);
|
||||
$prodattrval->fetch($this->fk_prod_attr_val);
|
||||
|
||||
return $prodattr->label.': '.$prodattrval->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a product combination 2 value pair
|
||||
* @return int <0 KO, >0 OK
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_combination2val
|
||||
(fk_prod_combination, fk_prod_attr, fk_prod_attr_val)
|
||||
VALUES(".(int) $this->fk_prod_combination.", ".(int) $this->fk_prod_attr.", ".(int) $this->fk_prod_attr_val.")";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if ($query) {
|
||||
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_combination2val');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a product combination 2 value pair from its rowid
|
||||
*
|
||||
* @param int $fk_combination Fk combination to search
|
||||
* @return int|ProductCombination2ValuePair[] -1 if KO
|
||||
*/
|
||||
public function fetchByFkCombination($fk_combination)
|
||||
{
|
||||
$sql = "SELECT
|
||||
c.rowid,
|
||||
c2v.fk_prod_attr_val,
|
||||
c2v.fk_prod_attr,
|
||||
c2v.fk_prod_combination
|
||||
FROM ".MAIN_DB_PREFIX."product_attribute c LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination2val c2v ON c.rowid = c2v.fk_prod_attr
|
||||
WHERE c2v.fk_prod_combination = ".(int) $fk_combination;
|
||||
|
||||
$sql .= $this->db->order('c.rang', 'asc');
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
if (!$query) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$return = array();
|
||||
|
||||
while ($result = $this->db->fetch_object($query)) {
|
||||
$tmp = new ProductCombination2ValuePair($this->db);
|
||||
$tmp->fk_prod_attr_val = $result->fk_prod_attr_val;
|
||||
$tmp->fk_prod_attr = $result->fk_prod_attr;
|
||||
$tmp->fk_prod_combination = $result->fk_prod_combination;
|
||||
$tmp->id = $result->rowid;
|
||||
|
||||
$return[] = $tmp;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a product combination 2 value pair
|
||||
*
|
||||
* @param int $fk_combination Rowid of the combination
|
||||
* @return int >0 OK <0 KO
|
||||
*/
|
||||
public function deleteByFkCombination($fk_combination)
|
||||
{
|
||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_combination2val WHERE fk_prod_combination = ".(int) $fk_combination;
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
0
htdocs/attributes/class/index.html
Normal file
0
htdocs/attributes/class/index.html
Normal file
641
htdocs/attributes/combinations.php
Normal file
641
htdocs/attributes/combinations.php
Normal file
@ -0,0 +1,641 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php';
|
||||
|
||||
$langs->load("products");
|
||||
$langs->load("other");
|
||||
|
||||
$var = false;
|
||||
$id = GETPOST('id', 'int');
|
||||
$valueid = GETPOST('valueid', 'int');
|
||||
$ref = GETPOST('ref');
|
||||
$weight_impact = (float) GETPOST('weight_impact');
|
||||
$price_impact = (float) GETPOST('price_impact');
|
||||
$price_impact_percent = (bool) GETPOST('price_impact_percent');
|
||||
$form = new Form($db);
|
||||
$action = GETPOST('action');
|
||||
|
||||
// Security check
|
||||
$fieldvalue = (! empty($id) ? $id : $ref);
|
||||
$fieldtype = (! empty($ref) ? 'ref' : 'rowid');
|
||||
$result=restrictedArea($user,'produit|service',$fieldvalue,'product&product','','',$fieldtype);
|
||||
|
||||
$prodstatic = new Product($db);
|
||||
$prodattr = new ProductAttribute($db);
|
||||
$prodattr_val = new ProductAttributeValue($db);
|
||||
|
||||
$product = new Product($db);
|
||||
|
||||
$product->fetch($id);
|
||||
|
||||
if (!$product->isProduct()) {
|
||||
header('Location: '.dol_buildpath('/product/card.php?id='.$product->id, 2));
|
||||
die;
|
||||
}
|
||||
|
||||
$prodcomb = new ProductCombination($db);
|
||||
$prodcomb2val = new ProductCombination2ValuePair($db);
|
||||
|
||||
$productCombination2ValuePairs1 = array();
|
||||
|
||||
if ($_POST) {
|
||||
|
||||
if ($action == 'add') {
|
||||
|
||||
$features = GETPOST('features', 'array');
|
||||
|
||||
if (!$features) {
|
||||
setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors');
|
||||
} else {
|
||||
$weight_impact = price2num($weight_impact);
|
||||
$price_impact = price2num($price_impact);
|
||||
$sanit_features = array();
|
||||
|
||||
//First, sanitize
|
||||
foreach ($features as $feature) {
|
||||
|
||||
$explode = explode(':', $feature);
|
||||
|
||||
if ($prodattr->fetch($explode[0]) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prodattr_val->fetch($explode[1]) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Valuepair
|
||||
$sanit_features[$explode[0]] = $explode[1];
|
||||
|
||||
$tmp = new ProductCombination2ValuePair($db);
|
||||
$tmp->fk_prod_attr = $explode[0];
|
||||
$tmp->fk_prod_attr_val = $explode[1];
|
||||
|
||||
$productCombination2ValuePairs1[] = $tmp;
|
||||
}
|
||||
|
||||
$db->begin();
|
||||
|
||||
if (!$prodcomb->fetchByProductCombination2ValuePairs($id, $sanit_features)) {
|
||||
if (ProductCombination::createProductCombination($product, $sanit_features, array(), $price_impact_percent, $price_impact, $weight_impact)) {
|
||||
$db->commit();
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$id, 2));
|
||||
die;
|
||||
} else {
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
}
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorRecordAlreadyExists'), 'errors');
|
||||
}
|
||||
|
||||
$db->rollback();
|
||||
}
|
||||
} elseif ($action == 'bulk_actions') {
|
||||
|
||||
$prodarray = array_keys(GETPOST('select', 'array'));
|
||||
$bulkaction = GETPOST('bulk_action');
|
||||
$error = 0;
|
||||
|
||||
$prodstatic = new Product($db);
|
||||
|
||||
$db->begin();
|
||||
|
||||
foreach ($prodarray as $prodid) {
|
||||
|
||||
if ($prodstatic->fetch($prodid) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($bulkaction == 'on_sell') {
|
||||
$prodstatic->status = 1;
|
||||
$res = $prodstatic->update($prodstatic->id, $user);
|
||||
} elseif ($bulkaction == 'on_buy') {
|
||||
$prodstatic->status_buy = 1;
|
||||
$res = $prodstatic->update($prodstatic->id, $user);
|
||||
} elseif ($bulkaction == 'not_sell') {
|
||||
$prodstatic->status = 0;
|
||||
$res = $prodstatic->update($prodstatic->id, $user);
|
||||
} elseif ($bulkaction == 'not_buy') {
|
||||
$prodstatic->status_buy = 0;
|
||||
$res = $prodstatic->update($prodstatic->id, $user);
|
||||
} elseif ($bulkaction == 'delete') {
|
||||
$res = $prodstatic->delete($prodstatic->id);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($res <= 0) {
|
||||
$error++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
$db->rollback();
|
||||
|
||||
if ($prodstatic->error) {
|
||||
setEventMessage($langs->trans($prodstatic->error), 'errors');
|
||||
} else {
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
}
|
||||
|
||||
} else {
|
||||
$db->commit();
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ($prodcomb->fetch($valueid) < 0) {
|
||||
dol_print_error($db, $langs->trans('ErrorRecordNotFound'));
|
||||
die;
|
||||
}
|
||||
|
||||
$prodcomb->variation_price_percentage = $price_impact_percent;
|
||||
$prodcomb->variation_price = $price_impact;
|
||||
$prodcomb->variation_weight = $weight_impact;
|
||||
|
||||
if ($prodcomb->update() > 0) {
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$id, 2));
|
||||
die;
|
||||
} else {
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$productCombinations = $prodcomb->fetchAllByFkProductParent($id);
|
||||
|
||||
if ($action === 'confirm_deletecombination') {
|
||||
|
||||
if ($prodcomb->fetch($valueid) > 0) {
|
||||
|
||||
$db->begin();
|
||||
|
||||
if ($prodcomb->delete() > 0 && $prodstatic->fetch($prodcomb->fk_product_child) > 0 && $prodstatic->delete() > 0) {
|
||||
$db->commit();
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$product->id, 2));
|
||||
die;
|
||||
}
|
||||
|
||||
$db->rollback();
|
||||
setEventMessage($langs->trans('ProductCombinationAlreadyUsed'), 'errors');
|
||||
$action = '';
|
||||
}
|
||||
} elseif ($action === 'edit') {
|
||||
|
||||
if ($prodcomb->fetch($valueid) < 0) {
|
||||
dol_print_error($db, $langs->trans('ErrorRecordNotFound'));
|
||||
die;
|
||||
}
|
||||
|
||||
$weight_impact = $prodcomb->variation_weight;
|
||||
$price_impact = $prodcomb->variation_price;
|
||||
$price_impact_percent = $prodcomb->variation_price_percentage;
|
||||
|
||||
$productCombination2ValuePairs1 = $prodcomb2val->fetchByFkCombination($valueid);
|
||||
} elseif ($action === 'confirm_copycombination') {
|
||||
|
||||
//Check destination product
|
||||
$dest_product = GETPOST('dest_product');
|
||||
|
||||
if ($prodstatic->fetch('', $dest_product) > 0) {
|
||||
|
||||
//To prevent from copying to the same product
|
||||
if ($prodstatic->ref != $product->ref) {
|
||||
if ($prodcomb->copyAll($product->id, $prodstatic) > 0) {
|
||||
header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$prodstatic->id, 2));
|
||||
die;
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorCopyProductCombinations'), 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorDestinationProductNotFound'), 'errors');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
if (! empty($id) || ! empty($ref)) {
|
||||
$object = new Product($db);
|
||||
$result = $object->fetch($id, $ref);
|
||||
|
||||
llxHeader("", "", $langs->trans("CardProduct".$object->type));
|
||||
|
||||
if ($result) {
|
||||
$head = product_prepare_head($object);
|
||||
$titre = $langs->trans("CardProduct".$object->type);
|
||||
$picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
|
||||
|
||||
dol_fiche_head($head, 'combinations', $titre, 0, $picto);
|
||||
|
||||
print '<table class="border" width="100%">';
|
||||
|
||||
// Reference
|
||||
print '<tr>';
|
||||
print '<td width="30%">'.$langs->trans("Ref").'</td><td colspan="3">';
|
||||
print $form->showrefnav($object, 'id', '', 0);
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Label
|
||||
print '<tr><td>'.$langs->trans("Label").'</td><td colspan="3">'.$object->label.'</td></tr>';
|
||||
|
||||
// Status (to sell)
|
||||
print '<tr><td>'.$langs->trans("Status").' ('.$langs->trans("Sell").')</td><td>';
|
||||
print $object->getLibStatut(2, 0);
|
||||
print '</td></tr>';
|
||||
|
||||
// Status (to buy)
|
||||
print '<tr><td>'.$langs->trans("Status").' ('.$langs->trans("Buy").')</td><td>';
|
||||
print $object->getLibStatut(2, 1);
|
||||
print '</td></tr>';
|
||||
|
||||
print '</table>';
|
||||
|
||||
dol_fiche_end();
|
||||
}
|
||||
|
||||
if ($action == 'add' || ($action == 'edit')) {
|
||||
|
||||
if ($action == 'add') {
|
||||
$title = $langs->trans('NewProductCombination');
|
||||
} else {
|
||||
$title = $langs->trans('EditProductCombination');
|
||||
}
|
||||
|
||||
print_fiche_titre($title);
|
||||
|
||||
if ($action == 'add') {
|
||||
$prodattr_all = $prodattr->fetchAll();
|
||||
|
||||
if (!$selected) {
|
||||
$selected = $prodattr_all[key($prodattr_all)]->id;
|
||||
}
|
||||
|
||||
$prodattr_alljson = array();
|
||||
|
||||
foreach ($prodattr_all as $each) {
|
||||
$prodattr_alljson[$each->id] = $each;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<script>
|
||||
|
||||
attributes_available = <?php echo json_encode($prodattr_alljson) ?>;
|
||||
attributes_selected = {
|
||||
index: [],
|
||||
info: []
|
||||
};
|
||||
|
||||
<?php foreach ($productCombination2ValuePairs1 as $pc2v):
|
||||
$prodattr_val->fetch($pc2v->fk_prod_attr_val);
|
||||
?>
|
||||
attributes_selected.index.push(<?php echo $pc2v->fk_prod_attr ?>);
|
||||
attributes_selected.info[<?php echo $pc2v->fk_prod_attr ?>] = {
|
||||
attribute: attributes_available[<?php echo $pc2v->fk_prod_attr ?>],
|
||||
value: {
|
||||
id: <?php echo $pc2v->fk_prod_attr_val ?>,
|
||||
label: '<?php echo $prodattr_val->value ?>'
|
||||
}
|
||||
};
|
||||
<?php endforeach ?>
|
||||
|
||||
restoreAttributes = function() {
|
||||
jQuery("select[name=attribute]").empty().append('<option value=""></option>');
|
||||
|
||||
jQuery.each(attributes_available, function (key, val) {
|
||||
if (jQuery.inArray(val.id, attributes_selected.index) == -1) {
|
||||
jQuery("select[name=attribute]").append('<option value="' + val.id + '">' + val.label + '</option>');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
paintAttributes = function() {
|
||||
var select = jQuery("select#features");
|
||||
|
||||
select.empty();
|
||||
jQuery("form#combinationform input[type=hidden]").detach();
|
||||
|
||||
jQuery.each(attributes_selected.index, function (key, val) {
|
||||
var attr_info = attributes_selected.info[val];
|
||||
|
||||
var opt_key = val + ':' + attr_info.value.id;
|
||||
var opt_label = attr_info.attribute.label + ': ' + attr_info.value.label;
|
||||
|
||||
//Add combination to the list
|
||||
select.append('<option value="' + opt_key + '">' + opt_label + '</option>');
|
||||
//Add hidden input to catch the new combination
|
||||
jQuery("form#combinationform").append('<input type="hidden" name="features[]" value="' + opt_key + '">');
|
||||
});
|
||||
};
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
|
||||
jQuery("select#attribute").change(function () {
|
||||
|
||||
var select = jQuery("select#value");
|
||||
|
||||
if (!jQuery(this).val().length) {
|
||||
select.empty();
|
||||
return;
|
||||
}
|
||||
|
||||
select.empty().append('<option value="">Loading...</option>');
|
||||
|
||||
jQuery.getJSON("<?php echo dol_buildpath('/attributes/ajax/get_attribute_values.php', 2) ?>", {
|
||||
id: jQuery(this).val()
|
||||
}, function(data) {
|
||||
if (data.error) {
|
||||
jQuery("select#value").empty();
|
||||
return alert(data.error);
|
||||
}
|
||||
|
||||
select.empty();
|
||||
|
||||
jQuery(data).each(function (key, val) {
|
||||
jQuery("select#value").append('<option value="' + val.id + '">' + val.value + '</option>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
jQuery("#addfeature").click(function () {
|
||||
var selectedattr = jQuery("select[name=attribute] option:selected");
|
||||
var selectedvalu = jQuery("select[name=value] option:selected");
|
||||
|
||||
if (!selectedattr.val().length || !selectedvalu.val().length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedattr_val = parseInt(selectedattr.val());
|
||||
|
||||
if (jQuery.inArray(selectedattr_val, attributes_selected.index) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
attributes_selected.index.push(selectedattr_val);
|
||||
attributes_selected.info[selectedattr_val] = {
|
||||
attribute: attributes_available[selectedattr_val],
|
||||
value: {
|
||||
id: selectedvalu.val(),
|
||||
label: selectedvalu.html()
|
||||
}
|
||||
};
|
||||
|
||||
paintAttributes();
|
||||
|
||||
selectedattr.detach();
|
||||
jQuery("select[name=value] option").detach();
|
||||
});
|
||||
|
||||
jQuery("#delfeature").click(function() {
|
||||
jQuery("#features option:selected").each(function (key, val) {
|
||||
var explode = jQuery(val).val().split(':');
|
||||
var indexOf = attributes_selected.index.indexOf(parseInt(explode[0]));
|
||||
|
||||
if (indexOf != -1) {
|
||||
attributes_selected.index.splice(indexOf, 1);
|
||||
jQuery(attributes_selected.info[parseInt(explode[0])]).detach();
|
||||
}
|
||||
|
||||
jQuery(val).detach();
|
||||
});
|
||||
|
||||
restoreAttributes();
|
||||
paintAttributes();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php } ?>
|
||||
|
||||
<form method="post" id="combinationform">
|
||||
<table class="border" style="width: 100%">
|
||||
<?php if ($action == 'add'): ?>
|
||||
<tr>
|
||||
<td style="width: 25%"><label for="attribute"><?php echo $langs->trans('ProductAttribute') ?></label></td>
|
||||
<td colspan="2"><select id="attribute" name="attribute">
|
||||
<option value=""></option>
|
||||
<?php foreach ($prodattr_all as $attr): ?>
|
||||
<option value="<?php echo $attr->id ?>"><?php echo $attr->label ?></option>
|
||||
<?php endforeach ?>
|
||||
</select></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 25%"><label for="value"><?php echo $langs->trans('Value') ?></label></td>
|
||||
<td colspan="2">
|
||||
<select id="value" name="value">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr>
|
||||
<td style="width: 25%" class="fieldrequired"><label for="features"><?php echo $langs->trans('Features') ?></label></td>
|
||||
<td><select multiple style="width: 100%" id="features">
|
||||
<?php
|
||||
foreach ($productCombination2ValuePairs1 as $pc2v): ?>
|
||||
<option value="<?php echo $pc2v->fk_prod_attr ?>:<?php echo $pc2v->fk_prod_attr_val ?>"><?php echo dol_htmlentities($pc2v) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select></td>
|
||||
<td>
|
||||
<?php if ($action == 'add'): ?>
|
||||
<a href="#" class="button" id="addfeature"><?php echo img_edit_add() ?></a><br><br>
|
||||
<a href="#" class="button" id="delfeature"><?php echo img_edit_remove() ?></a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 25%"><label for="price_impact"><?php echo $langs->trans('PriceImpact') ?></label></td>
|
||||
<td colspan="2"><input type="text" id="price_impact" name="price_impact" value="<?php echo price($price_impact) ?>">
|
||||
<input type="checkbox" id="price_impact_percent" name="price_impact_percent" <?php echo $price_impact_percent ? ' checked' : '' ?>> <label for="price_impact_percent"><?php echo $langs->trans('PercentageVariation') ?></label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 25%"><label for="weight_impact"><?php echo $langs->trans('WeightImpact') ?></label></td>
|
||||
<td colspan="2"><input type="text" id="weight_impact" name="weight_impact" value="<?php echo price($weight_impact) ?>"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
<div style="text-align: center"><input type="submit" value="<?php echo $action == 'add' ? $langs->trans('Create') : $langs->trans('Save') ?>" class="button"></div>
|
||||
<?php foreach ($productCombination2ValuePairs1 as $pc2v): ?>
|
||||
<input type="hidden" name="features[]" value="<?php echo $pc2v->fk_prod_attr.':'.$pc2v->fk_prod_attr_val ?>">
|
||||
<?php endforeach; ?>
|
||||
</form>
|
||||
<?php
|
||||
|
||||
} else {
|
||||
|
||||
if ($action === 'delete') {
|
||||
|
||||
if ($prodcomb->fetch($valueid) > 0) {
|
||||
$form = new Form($db);
|
||||
$prodstatic->fetch($prodcomb->fk_product_child);
|
||||
|
||||
print $form->formconfirm(
|
||||
"combinations.php?id=".$id."&valueid=".$valueid,
|
||||
$langs->trans('Delete'),
|
||||
$langs->trans('ProductCombinationDeleteDialog', $prodstatic->getNomUrl(1)),
|
||||
"confirm_deletecombination",
|
||||
'',
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
} elseif ($action === 'copy') {
|
||||
|
||||
$form = new Form($db);
|
||||
|
||||
print $form->formconfirm(
|
||||
'combinations.php?id='.$id,
|
||||
$langs->trans('CloneCombinationsProduct'),
|
||||
$langs->trans('ConfirmCloneProductCombinations'),
|
||||
'confirm_copycombination',
|
||||
array(
|
||||
array(
|
||||
'type' => 'text',
|
||||
'label' => $langs->trans('CloneDestinationReference'),
|
||||
'name' => 'dest_product'
|
||||
)
|
||||
),
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
$comb2val = new ProductCombination2ValuePair($db);
|
||||
|
||||
if ($productCombinations): ?>
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(document).ready(function() {
|
||||
|
||||
jQuery('input[name="select_all"]').click(function() {
|
||||
|
||||
if (jQuery(this).prop('checked')) {
|
||||
var checked = true;
|
||||
} else {
|
||||
var checked = false;
|
||||
}
|
||||
|
||||
jQuery('table.liste input[type="checkbox"]').prop('checked', checked);
|
||||
});
|
||||
|
||||
jQuery('input[name^="select["]').click(function() {
|
||||
jQuery('input[name="select_all"]').prop('checked', false);
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<form method="post">
|
||||
<label for="bulk_action"><?php echo $langs->trans('BulkActions') ?></label>
|
||||
<select id="bulk_action" name="bulk_action" class="flat">
|
||||
<option value="not_buy"><?php echo $langs->trans('ProductStatusNotOnBuy') ?></option>
|
||||
<option value="not_sell"><?php echo $langs->trans('ProductStatusNotOnSell') ?></option>
|
||||
<option value="on_buy"><?php echo $langs->trans('ProductStatusOnBuy') ?></option>
|
||||
<option value="on_sell"><?php echo $langs->trans('ProductStatusOnSell') ?></option>
|
||||
<option value="delete"><?php echo $langs->trans('Delete') ?></option>
|
||||
</select>
|
||||
<input type="hidden" name="action" value="bulk_actions">
|
||||
<input type="submit" value="Aplicar" class="button">
|
||||
<br>
|
||||
<br>
|
||||
<?php endif; ?>
|
||||
|
||||
<table class="liste">
|
||||
<tr class="liste_titre">
|
||||
<th class="liste_titre">
|
||||
<?php if ($productCombinations): ?>
|
||||
<input type="checkbox" name="select_all">
|
||||
<?php endif ?>
|
||||
</th>
|
||||
<th class="liste_titre"><?php echo $langs->trans('Product') ?></th>
|
||||
<th class="liste_titre"><?php echo $langs->trans('Combination') ?></th>
|
||||
<th class="liste_titre" style="text-align: center"><?php echo $langs->trans('PriceImpact') ?></th>
|
||||
<th class="liste_titre" style="text-align: center"><?php echo $langs->trans('WeightImpact') ?></th>
|
||||
<th class="liste_titre" style="text-align: center;"><?php echo $langs->trans('OnSell') ?></th>
|
||||
<th class="liste_titre" style="text-align: center;"><?php echo $langs->trans('OnBuy') ?></th>
|
||||
<th class="liste_titre"></th>
|
||||
</tr>
|
||||
<?php foreach ($productCombinations as $currcomb):
|
||||
$prodstatic->fetch($currcomb->fk_product_child); ?>
|
||||
<tr <?php echo $bc[!$var] ?>>
|
||||
<td><input type="checkbox" name="select[<?php echo $prodstatic->id ?>]"></td>
|
||||
<td><?php echo $prodstatic->getNomUrl(1) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
|
||||
$productCombination2ValuePairs = $comb2val->fetchByFkCombination($currcomb->id);
|
||||
$iMax = count($productCombination2ValuePairs);
|
||||
|
||||
for ($i = 0; $i < $iMax; $i++) {
|
||||
echo dol_htmlentities($productCombination2ValuePairs[$i]);
|
||||
|
||||
if ($i !== ($iMax - 1)) {
|
||||
echo ', ';
|
||||
}
|
||||
} ?>
|
||||
</td>
|
||||
<td style="text-align: right"><?php echo ($currcomb->variation_price >= 0 ? '+' : '').price($currcomb->variation_price).($currcomb->variation_price_percentage ? ' %' : '') ?></td>
|
||||
<td style="text-align: right"><?php echo ($currcomb->variation_weight >= 0 ? '+' : '').price($currcomb->variation_weight).' '.measuring_units_string($prodstatic->weight_units, 'weight') ?></td>
|
||||
<td style="text-align: center;"><?php echo $prodstatic->getLibStatut(2, 0) ?></td>
|
||||
<td style="text-align: center;"><?php echo $prodstatic->getLibStatut(2, 1) ?></td>
|
||||
<td style="text-align: right">
|
||||
<a href="<?php echo dol_buildpath('/attributes/combinations.php?id='.$id.'&action=edit&valueid='.$currcomb->id, 2) ?>"><?php echo img_edit() ?></a>
|
||||
<a href="<?php echo dol_buildpath('/attributes/combinations.php?id='.$id.'&action=delete&valueid='.$currcomb->id, 2) ?>"><?php echo img_delete() ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php $var = !$var; endforeach ?>
|
||||
</table>
|
||||
|
||||
<?php if ($productCombinations): ?>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
|
||||
<?php
|
||||
|
||||
print '<div class="tabsAction">';
|
||||
print ' <div class="inline-block divButAction">';
|
||||
if ($productCombinations) {
|
||||
print ' <a href="combinations.php?id='.$id.'&action=copy" class="butAction">'.$langs->trans('Copy').'</a>';
|
||||
}
|
||||
print ' <a href="generator.php?id='.$id.'" class="butAction">'.$langs->trans('ProductCombinationGenerator').'</a>
|
||||
<a href="combinations.php?id='.$id.'&action=add" class="butAction">'.$langs->trans('NewProductCombination').'</a>';
|
||||
print ' </div>';
|
||||
print '</div>';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
llxFooter();
|
||||
73
htdocs/attributes/create.php
Normal file
73
htdocs/attributes/create.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
require '../main.inc.php';
|
||||
require 'class/ProductAttribute.class.php';
|
||||
|
||||
$ref = GETPOST('ref');
|
||||
$label = GETPOST('label');
|
||||
|
||||
if ($_POST) {
|
||||
|
||||
if (empty($ref) || empty($label)) {
|
||||
setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors');
|
||||
} else {
|
||||
|
||||
$prodattr = new ProductAttribute($db);
|
||||
$prodattr->label = $label;
|
||||
$prodattr->ref = $ref;
|
||||
|
||||
if ($prodattr->create()) {
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
header('Location: '.dol_buildpath('/attributes/list.php', 2));
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorRecordAlreadyExists'), 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$langs->load('products');
|
||||
|
||||
$title = $langs->trans('NewProductAttribute');
|
||||
|
||||
llxHeader('', $title);
|
||||
|
||||
print_fiche_titre($title);
|
||||
|
||||
dol_fiche_head();
|
||||
|
||||
?>
|
||||
<form method="post">
|
||||
<table class="border" style="width: 100%">
|
||||
<tr>
|
||||
<td class="fieldrequired"><label for="ref"><?php echo $langs->trans('Ref') ?></label></td>
|
||||
<td><input type="text" id="ref" name="ref" value="<?php echo $ref ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fieldrequired"><label for="label"><?php echo $langs->trans('Label') ?></label></td>
|
||||
<td><input type="text" id="label" name="label" value="<?php echo $label ?>"></td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<?php
|
||||
dol_fiche_end();
|
||||
|
||||
print '<div class="center"><input type="submit" class="button" value="'.$langs->trans("Create").'"></div></form>';
|
||||
|
||||
llxFooter();
|
||||
102
htdocs/attributes/create_val.php
Normal file
102
htdocs/attributes/create_val.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
require '../main.inc.php';
|
||||
require 'class/ProductAttribute.class.php';
|
||||
require 'class/ProductAttributeValue.class.php';
|
||||
|
||||
$id = GETPOST('id');
|
||||
$ref = GETPOST('ref');
|
||||
$value = GETPOST('value');
|
||||
|
||||
$prodattr = new ProductAttribute($db);
|
||||
$prodattrval = new ProductAttributeValue($db);
|
||||
|
||||
if ($prodattr->fetch($id) < 1) {
|
||||
dol_print_error($db, $langs->trans('ErrorRecordNotFound'));
|
||||
die;
|
||||
}
|
||||
|
||||
if ($_POST) {
|
||||
|
||||
if (empty($ref) || empty($value)) {
|
||||
setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors');
|
||||
} else {
|
||||
|
||||
$prodattrval->fk_product_attribute = $prodattr->id;
|
||||
$prodattrval->ref = $ref;
|
||||
$prodattrval->value = $value;
|
||||
|
||||
if ($prodattrval->create() > 0) {
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2));
|
||||
die;
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorCreatingProductAttributeValue'), 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$langs->load('products');
|
||||
|
||||
$title = $langs->trans('ProductAttributeName', dol_htmlentities($prodattr->label));
|
||||
|
||||
llxHeader('', $title);
|
||||
|
||||
print_fiche_titre($title);
|
||||
|
||||
dol_fiche_head();
|
||||
|
||||
?>
|
||||
<table class="border" style="width: 100%">
|
||||
<tr>
|
||||
<td style="width: 15%" class="fieldrequired"><?php echo $langs->trans('Ref') ?></td>
|
||||
<td><?php echo dol_htmlentities($prodattr->ref) ?>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 15%" class="fieldrequired"><?php echo $langs->trans('Label') ?></td>
|
||||
<td><?php echo dol_htmlentities($prodattr->label) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php
|
||||
|
||||
dol_fiche_end();
|
||||
|
||||
print_fiche_titre($langs->trans('NewProductAttributeValue'));
|
||||
|
||||
dol_fiche_head();
|
||||
?>
|
||||
<form method="post">
|
||||
<table class="border" style="width: 100%">
|
||||
<tr>
|
||||
<td style="width: 15%" class="fieldrequired"><label for="ref"><?php echo $langs->trans('Ref') ?></label></td>
|
||||
<td><input id="ref" type="text" name="ref" value="<?php echo $ref ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 15%" class="fieldrequired"><label for="value"><?php echo $langs->trans('Value') ?></label></td>
|
||||
<td><input id="value" type="text" name="value" value="<?php echo $value ?>"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php
|
||||
dol_fiche_end();
|
||||
|
||||
print '<div class="center"><input type="submit" class="button" value="'.$langs->trans("Create").'"></div></form>';
|
||||
|
||||
llxFooter();
|
||||
393
htdocs/attributes/generator.php
Normal file
393
htdocs/attributes/generator.php
Normal file
@ -0,0 +1,393 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php';
|
||||
|
||||
$langs->load("products");
|
||||
$langs->load('other');
|
||||
|
||||
$id = GETPOST('id', 'int');
|
||||
$ref = GETPOST('ref');
|
||||
$form = new Form($db);
|
||||
|
||||
// Security check
|
||||
$fieldvalue = (! empty($id) ? $id : $ref);
|
||||
$fieldtype = (! empty($ref) ? 'ref' : 'rowid');
|
||||
$result=restrictedArea($user,'produit|service',$fieldvalue,'product&product','','',$fieldtype);
|
||||
|
||||
$prodattr = new ProductAttribute($db);
|
||||
$prodattrval = new ProductAttributeValue($db);
|
||||
$product = new Product($db);
|
||||
|
||||
$product->fetch($id);
|
||||
|
||||
if (!$product->isProduct()) {
|
||||
header('Location: '.dol_buildpath('/product/card.php?id='.$product->id, 2));
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Posible combinations. Format.
|
||||
* attrval => array(
|
||||
* valueid => array(
|
||||
* price => ''
|
||||
* weight => ''
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
$combinations = GETPOST('combinations', 'array');
|
||||
$price_var_percent = (bool) GETPOST('price_var_percent');
|
||||
$donotremove = true;
|
||||
|
||||
if ($_POST) {
|
||||
|
||||
$donotremove = (bool) GETPOST('donotremove');
|
||||
|
||||
//We must check if all those given combinations actually exist
|
||||
$sanitized_values = array();
|
||||
|
||||
foreach ($combinations as $attr => $val) {
|
||||
if ($prodattr->fetch($attr) > 0) {
|
||||
foreach ($val as $valueid => $content) {
|
||||
if ($prodattrval->fetch($valueid) > 0) {
|
||||
$sanitized_values[$attr][$valueid] = $content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($sanitized_values) {
|
||||
|
||||
require DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
|
||||
|
||||
$adapted_values = array();
|
||||
|
||||
//Adapt the array to the cartesian function
|
||||
foreach ($sanitized_values as $attr => $val) {
|
||||
$adapted_values[$attr] = array_keys($val);
|
||||
}
|
||||
|
||||
$db->begin();
|
||||
|
||||
$combination = new ProductCombination($db);
|
||||
|
||||
$delete_prev_comb_res = 1;
|
||||
|
||||
if (!$donotremove) {
|
||||
$delete_prev_comb_res = $combination->deleteByFkProductParent($id);
|
||||
}
|
||||
|
||||
//Current combinations will be deleted
|
||||
if ($delete_prev_comb_res > 0) {
|
||||
|
||||
$res = 1;
|
||||
|
||||
foreach (cartesianArray($adapted_values) as $currcomb) {
|
||||
|
||||
$res = ProductCombination::createProductCombination($product, $currcomb, $sanitized_values, $price_var_percent);
|
||||
|
||||
if ($res < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($res > 0) {
|
||||
$db->commit();
|
||||
setEventMessage($langs->trans('RecordSaved'));
|
||||
header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$id, 2));
|
||||
die;
|
||||
} else {
|
||||
setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
|
||||
}
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorDeletingGeneratedProducts'), 'errors');
|
||||
}
|
||||
|
||||
$db->rollback();
|
||||
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
if (! empty($id) || ! empty($ref)) {
|
||||
$object = new Product($db);
|
||||
$result = $object->fetch($id, $ref);
|
||||
|
||||
llxHeader("", "", $langs->trans("CardProduct".$object->type));
|
||||
|
||||
if ($result) {
|
||||
$head = product_prepare_head($object);
|
||||
$titre = $langs->trans("CardProduct".$object->type);
|
||||
$picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
|
||||
|
||||
dol_fiche_head($head, 'combinations', $titre, 0, $picto);
|
||||
|
||||
print '<table class="border" width="100%">';
|
||||
|
||||
// Reference
|
||||
print '<tr>';
|
||||
print '<td width="30%">'.$langs->trans("Ref").'</td><td colspan="3">';
|
||||
print $form->showrefnav($object, 'id', '', 0);
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Label
|
||||
print '<tr><td>'.$langs->trans("Label").'</td><td colspan="3">'.$object->label.'</td></tr>';
|
||||
|
||||
// Status (to sell)
|
||||
print '<tr><td>'.$langs->trans("Status").' ('.$langs->trans("Sell").')</td><td>';
|
||||
print $object->getLibStatut(2, 0);
|
||||
print '</td></tr>';
|
||||
|
||||
// Status (to buy)
|
||||
print '<tr><td>'.$langs->trans("Status").' ('.$langs->trans("Buy").')</td><td>';
|
||||
print $object->getLibStatut(2, 1);
|
||||
print '</td></tr>';
|
||||
|
||||
print '</table>';
|
||||
|
||||
dol_fiche_end();
|
||||
}
|
||||
|
||||
print_fiche_titre($langs->trans('ProductCombinationGenerator'));
|
||||
|
||||
$dictionnary_attr = array();
|
||||
|
||||
foreach ($prodattr->fetchAll() as $attr) {
|
||||
$dictionnary_attr[$attr->id] = $attr;
|
||||
foreach ($prodattrval->fetchAllByProductAttribute($attr->id) as $attrval) {
|
||||
$dictionnary_attr[$attr->id]->values[$attrval->id] = $attrval;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<script>
|
||||
|
||||
dictionnary_attr = <?php echo json_encode($dictionnary_attr) ?>;
|
||||
weight_units = '<?php echo measuring_units_string($object->weight_units, 'weight') ?>';
|
||||
attr_selected = {};
|
||||
percentage_variation = jQuery('input#price_var_percent').prop('checked');
|
||||
|
||||
function parseSelectedFeatures(attr, val, inputs) {
|
||||
|
||||
var price = '';
|
||||
var weight = '';
|
||||
|
||||
if (typeof(inputs) == 'object') {
|
||||
price = inputs.price;
|
||||
weight = inputs.weight;
|
||||
}
|
||||
|
||||
if (!attr_selected.hasOwnProperty(attr)) {
|
||||
|
||||
var label = dictionnary_attr[attr].label;
|
||||
|
||||
var table = jQuery(document.createElement('table'))
|
||||
.attr('id', 'combinations_'+attr)
|
||||
.css('width', '100%')
|
||||
.addClass('liste')
|
||||
.append(
|
||||
jQuery(document.createElement('thead'))
|
||||
.append(jQuery(document.createElement('tr'))
|
||||
.addClass('liste_titre')
|
||||
.append(
|
||||
jQuery(document.createElement('th'))
|
||||
.addClass('liste_titre')
|
||||
.css('width', '40%')
|
||||
.text(label),
|
||||
jQuery(document.createElement('th')).addClass('liste_titre').css('text-align', 'center').html('<?php echo $langs->trans('PriceImpact') ?>'),
|
||||
jQuery(document.createElement('th')).addClass('liste_titre').css('text-align', 'center').html('<?php echo $langs->trans('WeightImpact') ?>')
|
||||
)
|
||||
)
|
||||
)
|
||||
;
|
||||
|
||||
|
||||
jQuery('form#combinationsform').prepend(table);
|
||||
|
||||
attr_selected[attr] = [];
|
||||
} else {
|
||||
if (jQuery.inArray(val, attr_selected[attr]) != -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var combinations_table = jQuery('table#combinations_' + attr);
|
||||
var html = jQuery(document.createElement('tr'));
|
||||
|
||||
if (combinations_table.children('tbody').children('tr').length % 2 === 0) {
|
||||
html.addClass('pair');
|
||||
} else {
|
||||
html.addClass('impair');
|
||||
}
|
||||
|
||||
var percent_symbol_html = jQuery(document.createElement('span')).attr('id', 'percentsymbol').html(' %');
|
||||
|
||||
if (!percentage_variation) {
|
||||
percent_symbol_html.hide();
|
||||
}
|
||||
|
||||
html.append(
|
||||
jQuery(document.createElement('td')).text(dictionnary_attr[attr].values[val].value),
|
||||
jQuery(document.createElement('td')).css('text-align', 'center').append(
|
||||
jQuery(document.createElement('input')).attr('type', 'text').css('width', '50px').attr('name', 'combinations[' + attr + '][' + val + '][price]').val(price),
|
||||
percent_symbol_html
|
||||
),
|
||||
jQuery(document.createElement('td')).css('text-align', 'center').append(
|
||||
jQuery(document.createElement('input')).attr('type', 'text').css('width', '50px').attr('name', 'combinations[' + attr + '][' + val + '][weight]').val(weight),
|
||||
' ' + weight_units
|
||||
)
|
||||
);
|
||||
|
||||
combinations_table.append(html);
|
||||
|
||||
attr_selected[attr].push(val);
|
||||
}
|
||||
|
||||
function showHidePercentageSymbol(checked) {
|
||||
|
||||
percentage_variation = checked;
|
||||
|
||||
if (checked) {
|
||||
jQuery('span#percentsymbol').show();
|
||||
} else {
|
||||
jQuery('span#percentsymbol').hide();
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
|
||||
var input_price_var_percent = jQuery('input#price_var_percent');
|
||||
|
||||
jQuery.each(<?php echo json_encode($combinations) ?>, function(key, val) {
|
||||
jQuery.each(val, function(valkey, valcontent) {
|
||||
parseSelectedFeatures(key, valkey, valcontent);
|
||||
});
|
||||
});
|
||||
|
||||
jQuery('#addfeature').click(function() {
|
||||
jQuery('#features option:selected').each(function(selector) {
|
||||
var explode = jQuery(this).val().split(':');
|
||||
parseSelectedFeatures(explode[0], explode[1]);
|
||||
});
|
||||
});
|
||||
|
||||
jQuery('#delfeature').click(function() {
|
||||
jQuery('#features option:selected').each(function(selector) {
|
||||
var explode = jQuery(this).val().split(':');
|
||||
|
||||
if (attr_selected.hasOwnProperty(explode[0])) {
|
||||
var tr = jQuery('input[name="combinations[' + explode[0] + '][' + explode[1] + '][price]"').parent().parent();
|
||||
|
||||
var index_value = jQuery.inArray(explode[1], attr_selected[explode[0]]);
|
||||
|
||||
attr_selected[explode[0]].splice(index_value, 1);
|
||||
|
||||
if (tr.parent().children('tr').length === 1) {
|
||||
tr.parent().parent().detach();
|
||||
delete attr_selected[explode[0]]
|
||||
} else {
|
||||
tr.detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
input_price_var_percent.click(function() {
|
||||
showHidePercentageSymbol(jQuery(this).prop('checked'));
|
||||
});
|
||||
|
||||
jQuery('input#donotremove').click(function() {
|
||||
if (jQuery(this).prop('checked')) {
|
||||
jQuery('div#info_donotremove').hide();
|
||||
} else {
|
||||
jQuery('div#info_donotremove').show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div style="width: 100%;display:block; height: 300px">
|
||||
|
||||
<div style="float:right; width: 79%; margin-left: 1%">
|
||||
|
||||
<form method="post" id="combinationsform">
|
||||
|
||||
<p><?php echo $langs->trans('TooMuchCombinationsWarning', $langs->trans('DoNotRemovePreviousCombinations')) ?></p>
|
||||
<input type="checkbox" name="price_var_percent"
|
||||
id="price_var_percent"<?php echo $price_var_percent ? ' checked' : '' ?>> <label
|
||||
for="price_var_percent"><?php echo $langs->trans('UsePercentageVariations') ?></label>
|
||||
<br>
|
||||
<input type="checkbox" name="donotremove"
|
||||
id="donotremove"<?php echo $donotremove ? ' checked' : '' ?>> <label for="donotremove"><?php echo $langs->trans('DoNotRemovePreviousCombinations') ?></label>
|
||||
|
||||
<br>
|
||||
<div id="info_donotremove" class="info" style="<?php echo $donotremove ? 'display: none' : '' ?>">
|
||||
<?php echo img_warning() ?>
|
||||
<?php echo $langs->trans('ProductCombinationGeneratorWarning') ?>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div style="text-align: center">
|
||||
<input type="submit" value="<?php echo $langs->trans('Generate') ?>" class="button" name="submit">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div style="float:left; width: 20%">
|
||||
|
||||
<select id="features" multiple style="width: 100%; height: 300px; overflow: auto">
|
||||
<?php foreach ($dictionnary_attr as $attr): ?>
|
||||
<optgroup label="<?php echo $attr->label ?>">
|
||||
<?php foreach ($attr->values as $attrval): ?>
|
||||
<option value="<?php echo $attr->id.':'.$attrval->id ?>"<?php
|
||||
if (isset($combinations[$attr->id][$attrval->id])) {
|
||||
echo ' selected';
|
||||
}
|
||||
?>><?php echo dol_htmlentities($attrval->value) ?></option>
|
||||
<?php endforeach ?>
|
||||
</optgroup>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
|
||||
<br><br>
|
||||
<a class="button" id="delfeature" style="float: right"><?php echo img_edit_remove() ?></a>
|
||||
<a class="button" id="addfeature"><?php echo img_edit_add() ?></a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
llxFooter();
|
||||
}
|
||||
0
htdocs/attributes/index.html
Normal file
0
htdocs/attributes/index.html
Normal file
0
htdocs/attributes/lib/index.html
Normal file
0
htdocs/attributes/lib/index.html
Normal file
31
htdocs/attributes/lib/product_attributes.lib.php
Normal file
31
htdocs/attributes/lib/product_attributes.lib.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the entity of the products.
|
||||
* @param DoliDB $db Database handler
|
||||
* @return mixed
|
||||
*/
|
||||
function getProductEntities(DoliDB $db)
|
||||
{
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||
|
||||
$product = new Product($db);
|
||||
|
||||
return getEntity($product->element, 1);
|
||||
}
|
||||
140
htdocs/attributes/list.php
Normal file
140
htdocs/attributes/list.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
require '../main.inc.php';
|
||||
require 'class/ProductAttribute.class.php';
|
||||
|
||||
$rowid = GETPOST('rowid');
|
||||
$action = GETPOST('action');
|
||||
$object = new ProductAttribute($db);
|
||||
|
||||
if ($action == 'up') {
|
||||
$object->fetch($rowid);
|
||||
$object->moveUp();
|
||||
|
||||
header('Location: '.$_SERVER['PHP_SELF']);
|
||||
die;
|
||||
} elseif ($action == 'down') {
|
||||
$object->fetch($rowid);
|
||||
$object->moveDown();
|
||||
|
||||
header('Location: '.$_SERVER['PHP_SELF']);
|
||||
die;
|
||||
}
|
||||
|
||||
$langs->load('products');
|
||||
|
||||
$var = false;
|
||||
$title = $langs->trans($langs->trans('ProductAttributes'));
|
||||
|
||||
$attributes = $object->fetchAll();
|
||||
|
||||
llxHeader('', $title);
|
||||
|
||||
print_fiche_titre($title);
|
||||
|
||||
$forcereloadpage=empty($conf->global->MAIN_FORCE_RELOAD_PAGE)?0:1;
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
$(".imgupforline, .imgdownforline").hide();
|
||||
$(".lineupdown").removeAttr('href');
|
||||
$(".tdlineupdown")
|
||||
.css("background-image", 'url(<?php echo DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/grip.png'; ?>)')
|
||||
.css("background-repeat", "no-repeat")
|
||||
.css("background-position", "center center")
|
||||
.hover(
|
||||
function () {
|
||||
$(this).addClass('showDragHandle');
|
||||
}, function () {
|
||||
$(this).removeClass('showDragHandle');
|
||||
}
|
||||
);
|
||||
|
||||
$("#tablelines").tableDnD({
|
||||
onDrop: function(table, row) {
|
||||
console.log('drop');
|
||||
var reloadpage = "<?php echo $forcereloadpage; ?>";
|
||||
var roworder = cleanSerialize($("#tablelines").tableDnDSerialize());
|
||||
$.post("<?php echo DOL_URL_ROOT; ?>/attributes/ajax/orderAttribute.php",
|
||||
{
|
||||
roworder: roworder
|
||||
},
|
||||
function() {
|
||||
if (reloadpage == 1) {
|
||||
location.href = '<?php echo $_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING']; ?>';
|
||||
} else {
|
||||
$("#tablelines .drag").each(
|
||||
function( intIndex ) {
|
||||
$(this).removeClass("pair impair");
|
||||
if (intIndex % 2 == 0) $(this).addClass('impair');
|
||||
if (intIndex % 2 == 1) $(this).addClass('pair');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
onDragClass: "dragClass",
|
||||
dragHandle: "tdlineupdown"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<table class="liste" id="tablelines">
|
||||
<tr class="liste_titre nodrag nodrop">
|
||||
<th class="liste_titre"><?php print $langs->trans('Ref') ?></th>
|
||||
<th class="liste_titre"><?php print $langs->trans('Label') ?></th>
|
||||
<th class="liste_titre"><?php print $langs->trans('NbProducts') ?></th>
|
||||
<th class="liste_titre" colspan="2"></th>
|
||||
</tr>
|
||||
<?php foreach ($attributes as $key => $attribute): ?>
|
||||
<tr id="row-<?php echo $attribute->id ?>" <?php echo $bcdd[$var] ?>>
|
||||
<td><a href="card.php?id=<?php echo $attribute->id ?>"><?php echo dol_htmlentities($attribute->ref) ?></a></td>
|
||||
<td><a href="card.php?id=<?php echo $attribute->id ?>"><?php echo dol_htmlentities($attribute->label) ?></a></td>
|
||||
<td><?php echo $attribute->countChildProducts() ?></td>
|
||||
<td style="text-align: right">
|
||||
<a href="card.php?id=<?php echo $attribute->id ?>&action=edit"><?php echo img_edit() ?></a>
|
||||
<a href="card.php?id=<?php echo $attribute->id ?>&action=delete"><?php echo img_delete() ?></a>
|
||||
</td>
|
||||
<td align="center" class="linecolmove tdlineupdown">
|
||||
<?php if ($key > 0): ?>
|
||||
<a class="lineupdown"
|
||||
href="<?php echo $_SERVER['PHP_SELF'] ?>?action=up&rowid=<?php echo $attribute->id ?>"><?php echo img_up('default', 0, 'imgupforline'); ?></a>
|
||||
<?php endif ?>
|
||||
<?php if ($key < count($attributes)-1): ?>
|
||||
<a class="lineupdown"
|
||||
href="<?php echo $_SERVER['PHP_SELF'] ?>?action=down&rowid=<?php echo $attribute->id ?>"><?php echo img_down('default', 0, 'imgdownforline'); ?></a>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
$var = !$var;
|
||||
endforeach
|
||||
?>
|
||||
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="tabsAction">
|
||||
<div class="inline-block divButAction">
|
||||
<a href="create.php" class="butAction"><?php echo $langs->trans('Create') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
llxFooter();
|
||||
@ -11,6 +11,7 @@
|
||||
* Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
|
||||
* Copyright (C) 2013-2014 Florian Henry <florian.henry@open-concept.pro>
|
||||
* Copyright (C) 2014 Ferran Marcet <fmarcet@2byte.es>
|
||||
* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* 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
|
||||
@ -50,6 +51,10 @@ if (! empty($conf->projet->enabled)) {
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
|
||||
}
|
||||
|
||||
if (!empty($conf->attributes->enabled)) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
}
|
||||
|
||||
$langs->load('companies');
|
||||
$langs->load('propal');
|
||||
$langs->load('compta');
|
||||
@ -682,7 +687,8 @@ if (empty($reshook))
|
||||
$product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):'');
|
||||
$price_ht = GETPOST('price_ht');
|
||||
$price_ht_devise = GETPOST('multicurrency_price_ht');
|
||||
if (GETPOST('prod_entry_mode') == 'free')
|
||||
$prod_entry_mode = GETPOST('prod_entry_mode');
|
||||
if ($prod_entry_mode == 'free')
|
||||
{
|
||||
$idprod=0;
|
||||
$tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
|
||||
@ -708,21 +714,35 @@ if (empty($reshook))
|
||||
}
|
||||
}
|
||||
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && GETPOST('type') < 0) {
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && GETPOST('type') < 0) {
|
||||
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
|
||||
$error ++;
|
||||
}
|
||||
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && $price_ht == '' && $price_ht_devise == '') // Unit price can be 0 but not ''. Also price can be negative for proposal.
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && $price_ht == '' && $price_ht_devise == '') // Unit price can be 0 but not ''. Also price can be negative for proposal.
|
||||
{
|
||||
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
|
||||
$error ++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) {
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && empty($product_desc)) {
|
||||
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Description")), null, 'errors');
|
||||
$error ++;
|
||||
}
|
||||
|
||||
if (!$error && !empty($conf->attributes->enabled) && $prod_entry_mode != 'free') {
|
||||
if ($combinations = GETPOST('combinations', 'array')) {
|
||||
//Check if there is a product with the given combination
|
||||
$prodcomb = new ProductCombination($db);
|
||||
|
||||
if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
|
||||
$idprod = $res->fk_product_child;
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorProductCombinationNotFound'), 'errors');
|
||||
$error ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! $error && ($qty >= 0) && (! empty($product_desc) || ! empty($idprod))) {
|
||||
$pu_ht = 0;
|
||||
$pu_ttc = 0;
|
||||
@ -961,7 +981,7 @@ if (empty($reshook))
|
||||
// Add buying price
|
||||
$fournprice = price2num(GETPOST('fournprice') ? GETPOST('fournprice') : '');
|
||||
$buyingprice = price2num(GETPOST('buying_price') != '' ? GETPOST('buying_price') : ''); // If buying_price is '0', we muste keep this value
|
||||
|
||||
|
||||
$pu_ht_devise = GETPOST('multicurrency_subprice');
|
||||
|
||||
$date_start = dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), GETPOST('date_startsec'), GETPOST('date_startmonth'), GETPOST('date_startday'), GETPOST('date_startyear'));
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Copyright (C) 2010-2013 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2011-2016 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
|
||||
* Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
|
||||
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
|
||||
* Copyright (C) 2014 Ferran Marcet <fmarcet@2byte.es>
|
||||
@ -51,6 +51,10 @@ if (! empty($conf->projet->enabled)) {
|
||||
}
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
|
||||
|
||||
if (!empty($conf->attributes->enabled)) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
}
|
||||
|
||||
$langs->load('orders');
|
||||
$langs->load('sendings');
|
||||
$langs->load('companies');
|
||||
@ -640,7 +644,8 @@ if (empty($reshook))
|
||||
$product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):'');
|
||||
$price_ht = GETPOST('price_ht');
|
||||
$price_ht_devise = GETPOST('multicurrency_price_ht');
|
||||
if (GETPOST('prod_entry_mode') == 'free')
|
||||
$prod_entry_mode = GETPOST('prod_entry_mode');
|
||||
if ($prod_entry_mode == 'free')
|
||||
{
|
||||
$idprod=0;
|
||||
$tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
|
||||
@ -670,11 +675,11 @@ if (empty($reshook))
|
||||
setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && GETPOST('type') < 0) {
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && GETPOST('type') < 0) {
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '') && (! ($price_ht_devise >= 0) || $price_ht_devise == '')) // Unit price can be 0 but not ''
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '') && (! ($price_ht_devise >= 0) || $price_ht_devise == '')) // Unit price can be 0 but not ''
|
||||
{
|
||||
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
|
||||
$error++;
|
||||
@ -683,11 +688,25 @@ if (empty($reshook))
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) {
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && empty($product_desc)) {
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
|
||||
if (!$error && !empty($conf->attributes->enabled) && $prod_entry_mode != 'free') {
|
||||
if ($combinations = GETPOST('combinations', 'array')) {
|
||||
//Check if there is a product with the given combination
|
||||
$prodcomb = new ProductCombination($db);
|
||||
|
||||
if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
|
||||
$idprod = $res->fk_product_child;
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorProductCombinationNotFound'), 'errors');
|
||||
$error ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! $error && ($qty >= 0) && (! empty($product_desc) || ! empty($idprod))) {
|
||||
// Clean parameters
|
||||
$date_start=dol_mktime(GETPOST('date_start'.$predef.'hour'), GETPOST('date_start'.$predef.'min'), GETPOST('date_start'.$predef.'sec'), GETPOST('date_start'.$predef.'month'), GETPOST('date_start'.$predef.'day'), GETPOST('date_start'.$predef.'year'));
|
||||
@ -2419,7 +2438,7 @@ if ($action == 'create' && $user->rights->commande->creer)
|
||||
}
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
|
||||
|
||||
print "</form>\n";
|
||||
|
||||
dol_fiche_end();
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
* Copyright (C) 2013-2014 Florian Henry <florian.henry@open-concept.pro>
|
||||
* Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
|
||||
* Copyright (C) 2014 Ferran Marcet <fmarcet@2byte.es>
|
||||
* Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2015-2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* 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
|
||||
@ -56,6 +56,10 @@ if (! empty($conf->projet->enabled)) {
|
||||
}
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
|
||||
|
||||
if (!empty($conf->attributes->enabled)) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
}
|
||||
|
||||
$langs->load('bills');
|
||||
$langs->load('companies');
|
||||
$langs->load('compta');
|
||||
@ -1362,7 +1366,8 @@ if (empty($reshook))
|
||||
$product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):'');
|
||||
$price_ht = GETPOST('price_ht');
|
||||
$price_ht_devise = GETPOST('multicurrency_price_ht');
|
||||
if (GETPOST('prod_entry_mode') == 'free')
|
||||
$prod_entry_mode = GETPOST('prod_entry_mode');
|
||||
if ($prod_entry_mode == 'free')
|
||||
{
|
||||
$idprod=0;
|
||||
$tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
|
||||
@ -1392,7 +1397,7 @@ if (empty($reshook))
|
||||
setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
|
||||
$error ++;
|
||||
}
|
||||
if (! GETPOST('prod_entry_mode'))
|
||||
if (!$prod_entry_mode)
|
||||
{
|
||||
if (GETPOST('type') < 0 && ! GETPOST('search_idprod'))
|
||||
{
|
||||
@ -1400,11 +1405,11 @@ if (empty($reshook))
|
||||
$error ++;
|
||||
}
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && GETPOST('type') < 0) {
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && GETPOST('type') < 0) {
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
|
||||
$error ++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '') && $price_ht_devise == '') // Unit price can be 0 but not ''
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '') && $price_ht_devise == '') // Unit price can be 0 but not ''
|
||||
{
|
||||
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
|
||||
$error ++;
|
||||
@ -1413,7 +1418,7 @@ if (empty($reshook))
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
|
||||
$error ++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) {
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && empty($product_desc)) {
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
|
||||
$error ++;
|
||||
}
|
||||
@ -1422,7 +1427,23 @@ if (empty($reshook))
|
||||
setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
|
||||
$error ++;
|
||||
}
|
||||
|
||||
if (!$error && !empty($conf->attributes->enabled) && $prod_entry_mode != 'free') {
|
||||
if ($combinations = GETPOST('combinations', 'array')) {
|
||||
//Check if there is a product with the given combination
|
||||
$prodcomb = new ProductCombination($db);
|
||||
|
||||
if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
|
||||
$idprod = $res->fk_product_child;
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorProductCombinationNotFound'), 'errors');
|
||||
$error ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! $error && ($qty >= 0) && (! empty($product_desc) || ! empty($idprod))) {
|
||||
|
||||
$ret = $object->fetch($id);
|
||||
if ($ret < 0) {
|
||||
dol_print_error($db, $object->error);
|
||||
@ -1755,7 +1776,7 @@ if (empty($reshook))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$result = $object->updateline(GETPOST('lineid'), $description, $pu_ht, $qty, GETPOST('remise_percent'),
|
||||
$date_start, $date_end, $vat_rate, $localtax1_rate, $localtax2_rate, 'HT', $info_bits, $type,
|
||||
GETPOST('fk_parent_line'), 0, $fournprice, $buyingprice, $label, $special_code, $array_options, GETPOST('progress'),
|
||||
@ -2147,7 +2168,7 @@ if ($action == 'create')
|
||||
print '</tr>' . "\n";
|
||||
|
||||
$exampletemplateinvoice=new FactureRec($db);
|
||||
|
||||
|
||||
// Overwrite value if creation of invoice is from a predefined invoice
|
||||
if (empty($origin) && empty($originid) && GETPOST('fac_rec','int') > 0)
|
||||
{
|
||||
@ -2464,7 +2485,7 @@ if ($action == 'create')
|
||||
}
|
||||
|
||||
$datefacture = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']);
|
||||
|
||||
|
||||
// Date invoice
|
||||
print '<tr><td class="fieldrequired">' . $langs->trans('DateInvoice') . '</td><td colspan="2">';
|
||||
print $form->select_date($datefacture?$datefacture:$dateinvoice, '', '', '', '', "add", 1, 1, 1);
|
||||
@ -2570,15 +2591,15 @@ if ($action == 'create')
|
||||
'__INVOICE_YEAR__' => $langs->trans("PreviousYearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date($dateexample,'%Y').')',
|
||||
'__INVOICE_NEXT_YEAR__' => $langs->trans("NextYearOfInvoice").' ('.$langs->trans("Example").': '.dol_print_date(dol_time_plus_duree($dateexample, 1, 'y'),'%Y').')'
|
||||
);
|
||||
|
||||
|
||||
$htmltext = '<i>'.$langs->trans("FollowingConstantsWillBeSubstituted").':<br>';
|
||||
foreach($substitutionarray as $key => $val)
|
||||
{
|
||||
$htmltext.=$key.' = '.$langs->trans($val).'<br>';
|
||||
}
|
||||
$htmltext.='</i>';
|
||||
$htmltext.='</i>';
|
||||
}
|
||||
|
||||
|
||||
// Public note
|
||||
print '<tr>';
|
||||
print '<td class="border tdtop">';
|
||||
@ -3501,7 +3522,7 @@ else if ($id > 0 || ! empty($ref))
|
||||
|
||||
print '</table>';
|
||||
|
||||
|
||||
|
||||
// List of previous situation invoices
|
||||
|
||||
$sign = 1;
|
||||
@ -3613,9 +3634,9 @@ else if ($id > 0 || ! empty($ref))
|
||||
print '</table>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
// List of payments already done
|
||||
|
||||
|
||||
print '<table class="noborder paymenttable" width="100%">';
|
||||
|
||||
print '<tr class="liste_titre">';
|
||||
@ -3924,7 +3945,7 @@ else if ($id > 0 || ! empty($ref))
|
||||
|
||||
print "</table>\n";
|
||||
print "</div>";
|
||||
|
||||
|
||||
print "</form>\n";
|
||||
|
||||
dol_fiche_end();
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Copyright (C) 2013 Christophe Battarel <christophe.battarel@altairis.fr>
|
||||
* Copyright (C) 2013-2014 Florian Henry <florian.henry@open-concept.pro>
|
||||
* Copyright (C) 2014-2016 Ferran Marcet <fmarcet@2byte.es>
|
||||
* Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -446,7 +446,7 @@ if (empty($reshook))
|
||||
unset($_POST["options_" . $key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (! $error)
|
||||
{
|
||||
// Clean parameters
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
* Copyright (C) 2010 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2010-2014 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2011 Herve Prot <herve.prot@symeos.com>
|
||||
* Copyright (C) 2012-2014 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
|
||||
* Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
|
||||
* Copyright (C) 2014 Alexandre Spangaro <aspangaro.dolibarr@gmail.com>
|
||||
@ -80,7 +80,7 @@ class Form
|
||||
* @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
|
||||
* @param string $moreparam More param to add on a href URL.
|
||||
* @param int $fieldrequired 1 if we want to show field as mandatory using the "fieldrequired" CSS.
|
||||
* @param int $notabletag 1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' '
|
||||
* @param int $notabletag 1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' '
|
||||
* @return string HTML edit field
|
||||
*/
|
||||
function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata='string', $moreparam='', $fieldrequired=0, $notabletag=0)
|
||||
@ -512,7 +512,7 @@ class Form
|
||||
*
|
||||
* @param string $selected Value auto selected when at least one record is selected. Not a preselected value. Use '0' by default.
|
||||
* @param int $arrayofaction array('code'=>'label', ...). The code is the key stored into the GETPOST('massaction') when submitting action.
|
||||
* @param int $alwaysvisible 1=select button always visible
|
||||
* @param int $alwaysvisible 1=select button always visible
|
||||
* @return string Select list
|
||||
*/
|
||||
function selectMassAction($selected, $arrayofaction, $alwaysvisible=0)
|
||||
@ -586,7 +586,7 @@ class Form
|
||||
</script>
|
||||
';
|
||||
}
|
||||
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@ -1410,7 +1410,7 @@ class Form
|
||||
// Build list includeUsers to have only hierarchy and current user
|
||||
$includeUsers = implode(",",$user->getAllChildIds(1));
|
||||
}
|
||||
|
||||
|
||||
$out='';
|
||||
|
||||
// On recherche les utilisateurs
|
||||
@ -1616,7 +1616,7 @@ class Form
|
||||
$i++;
|
||||
}
|
||||
if ($nbassignetouser) $out.='</div>';
|
||||
|
||||
|
||||
//$out.='</form>';
|
||||
return $out;
|
||||
}
|
||||
@ -1642,11 +1642,12 @@ class Form
|
||||
* @param int $hidepriceinlabel 1=Hide prices in label
|
||||
* @param string $warehouseStatus warehouse status filter, following comma separated filter options can be used
|
||||
* 'warehouseopen' = select products from open warehouses,
|
||||
* 'warehouseclosed' = select products from closed warehouses,
|
||||
* 'warehouseclosed' = select products from closed warehouses,
|
||||
* 'warehouseinternal' = select products from warehouses for internal correct/transfer only
|
||||
* @param array $selected_combinations Selected combinations. Format: array([attrid] => attrval, [...])
|
||||
* @return void
|
||||
*/
|
||||
function select_produits($selected='', $htmlname='productid', $filtertype='', $limit=20, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0, $showempty='1', $forcecombo=0, $morecss='', $hidepriceinlabel=0, $warehouseStatus='')
|
||||
function select_produits($selected='', $htmlname='productid', $filtertype='', $limit=20, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0, $showempty='1', $forcecombo=0, $morecss='', $hidepriceinlabel=0, $warehouseStatus='', $selected_combinations = array())
|
||||
{
|
||||
global $langs,$conf;
|
||||
|
||||
@ -1671,6 +1672,80 @@ class Form
|
||||
$urloption.='&socid='.$socid;
|
||||
}
|
||||
print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
|
||||
|
||||
if (!empty($conf->attributes->enabled)) {
|
||||
?>
|
||||
<script>
|
||||
|
||||
selected = <?php echo json_encode($selected_combinations) ?>;
|
||||
combvalues = {};
|
||||
|
||||
jQuery(document).ready(function () {
|
||||
|
||||
jQuery("input[name='prod_entry_mode']").change(function () {
|
||||
if (jQuery(this).val() == 'free') {
|
||||
jQuery('div#attributes_box').empty();
|
||||
}
|
||||
});
|
||||
|
||||
jQuery("input#<?php echo $htmlname ?>").change(function () {
|
||||
|
||||
if (!jQuery(this).val()) {
|
||||
jQuery('div#attributes_box').empty();
|
||||
return;
|
||||
}
|
||||
|
||||
jQuery.getJSON("<?php echo dol_buildpath('/attributes/ajax/getCombinations.php', 2) ?>", {
|
||||
id: jQuery(this).val()
|
||||
}, function (data) {
|
||||
jQuery('div#attributes_box').empty();
|
||||
|
||||
jQuery.each(data, function (key, val) {
|
||||
|
||||
combvalues[val.id] = val.values;
|
||||
|
||||
var span = jQuery(document.createElement('div')).css({
|
||||
'display': 'table-row'
|
||||
});
|
||||
|
||||
span.append(
|
||||
jQuery(document.createElement('div')).text(val.label).css({
|
||||
'font-weight': 'bold',
|
||||
'display': 'table-cell',
|
||||
'text-align': 'right'
|
||||
})
|
||||
);
|
||||
|
||||
var html = jQuery(document.createElement('select')).attr('name', 'combinations[' + val.id + ']').css({
|
||||
'margin-left': '15px',
|
||||
'white-space': 'pre'
|
||||
}).append(
|
||||
jQuery(document.createElement('option')).val('')
|
||||
);
|
||||
|
||||
jQuery.each(combvalues[val.id], function (key, val) {
|
||||
var tag = jQuery(document.createElement('option')).val(val.id).html(val.value);
|
||||
|
||||
if (selected[val.fk_product_attribute] == val.id) {
|
||||
tag.attr('selected', 'selected');
|
||||
}
|
||||
|
||||
html.append(tag);
|
||||
});
|
||||
|
||||
span.append(html);
|
||||
jQuery('div#attributes_box').append(span);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
<?php if ($selected): ?>
|
||||
jQuery("input#<?php echo $htmlname ?>").change();
|
||||
<?php endif ?>
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
if (empty($hidelabel)) print $langs->trans("RefOrLabel").' : ';
|
||||
else if ($hidelabel > 1) {
|
||||
if (! empty($conf->global->MAIN_HTML5_PLACEHOLDER)) $placeholder=' placeholder="'.$langs->trans("RefOrLabel").'"';
|
||||
@ -1709,7 +1784,7 @@ class Form
|
||||
* @param int $hidepriceinlabel 1=Hide prices in label
|
||||
* @param string $warehouseStatus warehouse status filter, following comma separated filter options can be used
|
||||
* 'warehouseopen' = select products from open warehouses,
|
||||
* 'warehouseclosed' = select products from closed warehouses,
|
||||
* 'warehouseclosed' = select products from closed warehouses,
|
||||
* 'warehouseinternal' = select products from warehouses for internal correct/transfer only
|
||||
* @return array Array of keys for json
|
||||
*/
|
||||
@ -1724,19 +1799,19 @@ class Form
|
||||
if (! empty($warehouseStatus))
|
||||
{
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
|
||||
if (preg_match('/warehouseclosed/', $warehouseStatus))
|
||||
if (preg_match('/warehouseclosed/', $warehouseStatus))
|
||||
{
|
||||
$warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
|
||||
}
|
||||
if (preg_match('/warehouseopen/', $warehouseStatus))
|
||||
if (preg_match('/warehouseopen/', $warehouseStatus))
|
||||
{
|
||||
$warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
|
||||
}
|
||||
if (preg_match('/warehouseinternal/', $warehouseStatus))
|
||||
if (preg_match('/warehouseinternal/', $warehouseStatus))
|
||||
{
|
||||
$warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$selectFields = " p.rowid, p.label, p.ref, p.description, p.barcode, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.fk_price_expression";
|
||||
(count($warehouseStatusArray)) ? $selectFieldsGrouped = ", sum(ps.reel) as stock" : $selectFieldsGrouped = ", p.stock";
|
||||
@ -1775,7 +1850,7 @@ class Form
|
||||
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid";
|
||||
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid";
|
||||
}
|
||||
|
||||
|
||||
//Price by customer
|
||||
if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
|
||||
$sql.=" LEFT JOIN ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".$socid." AND pcp.fk_product=p.rowid";
|
||||
@ -1785,11 +1860,21 @@ class Form
|
||||
{
|
||||
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid AND pl.lang='". $langs->getDefaultLang() ."'";
|
||||
}
|
||||
|
||||
if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
|
||||
$sql .= " LEFT JOIN llx_product_attribute_combination pac ON pac.fk_product_child = p.rowid";
|
||||
}
|
||||
|
||||
$sql.= ' WHERE p.entity IN ('.getEntity('product', 1).')';
|
||||
if (count($warehouseStatusArray))
|
||||
{
|
||||
$sql.= ' AND (p.fk_product_type = 1 OR e.statut IN ('.implode(',',$warehouseStatusArray).'))';
|
||||
}
|
||||
|
||||
if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
|
||||
$sql .= " AND pac.rowid IS NULL";
|
||||
}
|
||||
|
||||
if ($finished == 0)
|
||||
{
|
||||
$sql.= " AND p.finished = ".$finished;
|
||||
@ -1842,7 +1927,7 @@ class Form
|
||||
$num = $this->db->num_rows($result);
|
||||
|
||||
$events=null;
|
||||
|
||||
|
||||
if ($conf->use_javascript_ajax && ! $forcecombo)
|
||||
{
|
||||
include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
|
||||
@ -1850,9 +1935,9 @@ class Form
|
||||
$out.= $comboenhancement;
|
||||
$nodatarole=($comboenhancement?' data-role="none"':'');
|
||||
}
|
||||
|
||||
|
||||
$out.='<select class="flat'.($morecss?' '.$morecss:'').'" name="'.$htmlname.'" id="'.$htmlname.'"'.$nodatarole.'>';
|
||||
|
||||
|
||||
$textifempty='';
|
||||
// Do not use textifempty = ' ' or ' ' here, or search on key will search on ' key'.
|
||||
//if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
|
||||
@ -2877,7 +2962,7 @@ class Form
|
||||
|
||||
// Set default value if not already set by caller
|
||||
if (empty($selected) && ! empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
|
||||
|
||||
|
||||
print '<select class="flat" name="'.$htmlname.'">';
|
||||
if ($addempty) print '<option value="0"> </option>';
|
||||
foreach($this->cache_conditions_paiements as $id => $arrayconditions)
|
||||
@ -3385,7 +3470,7 @@ class Form
|
||||
// Clean parameters
|
||||
$newselectedchoice=empty($selectedchoice)?"no":$selectedchoice;
|
||||
if ($conf->browser->layout == 'phone') $width='95%';
|
||||
|
||||
|
||||
if (is_array($formquestion) && ! empty($formquestion))
|
||||
{
|
||||
// First add hidden fields and value
|
||||
@ -3651,7 +3736,7 @@ class Form
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
|
||||
|
||||
$out='';
|
||||
|
||||
|
||||
$formproject=new FormProjets($this->db);
|
||||
|
||||
$langs->load("project");
|
||||
@ -3679,8 +3764,8 @@ class Form
|
||||
$out.=" ";
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($nooutput))
|
||||
|
||||
if (empty($nooutput))
|
||||
{
|
||||
print $out;
|
||||
return '';
|
||||
@ -4235,7 +4320,7 @@ class Form
|
||||
// Make select dynamic
|
||||
include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
|
||||
$out.= ajax_combobox($htmlname);
|
||||
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
@ -4430,13 +4515,13 @@ class Form
|
||||
$key.= $rate['nprtva'] ? '*': '';
|
||||
if ($mode > 0 && $rate['code']) $key.=' ('.$rate['code'].')';
|
||||
if ($mode < 0) $key = $rate['rowid'];
|
||||
|
||||
|
||||
$return.= '<option value="'.$key.'"';
|
||||
if (! $selectedfound)
|
||||
{
|
||||
if ($defaultcode) // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
|
||||
{
|
||||
if ($defaultcode == $rate['code'])
|
||||
if ($defaultcode == $rate['code'])
|
||||
{
|
||||
$return.= ' selected';
|
||||
$selectedfound=true;
|
||||
@ -4860,7 +4945,7 @@ class Form
|
||||
* @param string $morecss Add more class to css styles
|
||||
* @param int $addjscombo Add js combo
|
||||
* @param string $moreparamonempty Add more param on the empty option line. Not used if show_empty not set.
|
||||
* @param int $disablebademail Check if an email is found into value and if not disable and colorize entry.
|
||||
* @param int $disablebademail Check if an email is found into value and if not disable and colorize entry.
|
||||
* @return string HTML select string.
|
||||
* @see multiselectarray
|
||||
*/
|
||||
@ -4882,7 +4967,7 @@ class Form
|
||||
{
|
||||
$minLengthToAutocomplete=0;
|
||||
$tmpplugin=empty($conf->global->MAIN_USE_JQUERY_MULTISELECT)?(constant('REQUIRE_JQUERY_MULTISELECT')?constant('REQUIRE_JQUERY_MULTISELECT'):'select2'):$conf->global->MAIN_USE_JQUERY_MULTISELECT;
|
||||
|
||||
|
||||
// Enhance with select2
|
||||
$nodatarole='';
|
||||
if ($conf->use_javascript_ajax)
|
||||
@ -4909,7 +4994,7 @@ class Form
|
||||
// Translate
|
||||
if ($translate)
|
||||
{
|
||||
foreach($array as $key => $value)
|
||||
foreach($array as $key => $value)
|
||||
{
|
||||
$array[$key]=$langs->trans($value);
|
||||
}
|
||||
@ -5428,7 +5513,7 @@ class Form
|
||||
|
||||
$linktoelem='';
|
||||
$linktoelemlist='';
|
||||
|
||||
|
||||
if (! is_object($object->thirdparty)) $object->fetch_thirdparty();
|
||||
|
||||
$possiblelinks=array(
|
||||
@ -5441,9 +5526,9 @@ class Form
|
||||
'order_supplier'=>array('enabled'=>$conf->fournisseur->commande->enabled , 'perms'=>1, 'label'=>'LinkToSupplierOrder', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.fk_soc = ".$object->thirdparty->id),
|
||||
'invoice_supplier'=>array('enabled'=>$conf->fournisseur->facture->enabled , 'perms'=>1, 'label'=>'LinkToSupplierInvoice', 'sql'=>"SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."facture_fourn as t WHERE t.fk_soc = s.rowid AND t.fk_soc = ".$object->thirdparty->id)
|
||||
);
|
||||
|
||||
|
||||
global $action;
|
||||
|
||||
|
||||
// Can complet the possiblelink array
|
||||
$hookmanager->initHooks(array('commonobject'));
|
||||
$parameters=array();
|
||||
@ -5468,7 +5553,7 @@ class Form
|
||||
$num = 0;
|
||||
|
||||
if (empty($possiblelink['enabled'])) continue;
|
||||
|
||||
|
||||
if (! empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || ! in_array($key, $excludelinksto)))
|
||||
{
|
||||
print '<div id="'.$key.'list"'.(empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)?' style="display:none"':'').'>';
|
||||
@ -5722,10 +5807,10 @@ class Form
|
||||
if ($conf->browser->layout == 'phone') $ret.='<div class="floatleft">'.$morehtmlleft.'</div>'; // class="center" to have photo in middle
|
||||
else $ret.='<div class="inline-block floatleft">'.$morehtmlleft.'</div>';
|
||||
}
|
||||
|
||||
|
||||
//if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
|
||||
$ret.='<div class="inline-block floatleft valignmiddle refid'.(($shownav && ($previous_ref || $next_ref))?' refidpadding':'').'">';
|
||||
|
||||
|
||||
// For thirdparty, contact, user, member, the ref is the id, so we show something else
|
||||
if ($object->element == 'societe')
|
||||
{
|
||||
@ -5740,8 +5825,8 @@ class Form
|
||||
{
|
||||
$ret.=' '.$morehtmlref;
|
||||
}
|
||||
$ret.='</div>';
|
||||
|
||||
$ret.='</div>';
|
||||
|
||||
$ret.='</div><!-- End banner content -->';
|
||||
|
||||
return $ret;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
/* Copyright (C) 2008-2011 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2008-2012 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
|
||||
* Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2015 Ferran Marcet <fmarcet@2byte.es>
|
||||
* Copyright (C) 2015-2016 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
|
||||
*
|
||||
@ -2084,4 +2084,31 @@ function colorStringToArray($stringcolor,$colorifnotfound=array(88,88,88))
|
||||
return array(hexdec($reg[1]),hexdec($reg[2]),hexdec($reg[3]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the Cartesian product algorithm to an array
|
||||
* Source: http://stackoverflow.com/a/15973172
|
||||
*
|
||||
* @param array $input Array of products
|
||||
* @return array Array of combinations
|
||||
*/
|
||||
function cartesianArray(array $input) {
|
||||
// filter out empty values
|
||||
$input = array_filter($input);
|
||||
|
||||
$result = array(array());
|
||||
|
||||
foreach ($input as $key => $values) {
|
||||
$append = array();
|
||||
|
||||
foreach($result as $product) {
|
||||
foreach($values as $item) {
|
||||
$product[$key] = $item;
|
||||
$append[] = $product;
|
||||
}
|
||||
}
|
||||
|
||||
$result = $append;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
* Copyright (C) 2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
|
||||
* Copyright (C) 2009-2010 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
|
||||
* Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2015-2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* 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
|
||||
@ -94,6 +94,23 @@ function product_prepare_head($object)
|
||||
$head[$h][2] = 'referers';
|
||||
$h++;
|
||||
|
||||
if (!empty($conf->attributes->enabled) && $object->isProduct()) {
|
||||
|
||||
global $db;
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
|
||||
$prodcomb = new ProductCombination($db);
|
||||
|
||||
if ($prodcomb->fetchByFkProductChild($object->id) == -1) {
|
||||
$head[$h][0] = DOL_URL_ROOT."/attributes/combinations.php?id=".$object->id;
|
||||
$head[$h][1] = $langs->trans('ProductCombinations');
|
||||
$head[$h][2] = 'combinations';
|
||||
}
|
||||
|
||||
$h++;
|
||||
}
|
||||
|
||||
if ($object->isProduct() || ($object->isService() && ! empty($conf->global->STOCK_SUPPORTS_SERVICES))) // If physical product we can stock (or service with option)
|
||||
{
|
||||
if (! empty($conf->stock->enabled) && $user->rights->stock->lire)
|
||||
|
||||
137
htdocs/core/modules/modAttributes.class.php
Normal file
137
htdocs/core/modules/modAttributes.class.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
|
||||
* Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup produit Module product attributes
|
||||
* \brief Module to manage product combinations based on product attributes
|
||||
* \file htdocs/core/modules/modAttributes.class.php
|
||||
* \ingroup produit
|
||||
* \brief File to describe module to manage catalog of predefined products
|
||||
*/
|
||||
include_once DOL_DOCUMENT_ROOT .'/core/modules/DolibarrModules.class.php';
|
||||
|
||||
|
||||
/**
|
||||
* Description and activation class for module Product attributes
|
||||
*/
|
||||
class modAttributes extends DolibarrModules
|
||||
{
|
||||
/**
|
||||
* Constructor. Define names, constants, directories, boxes, permissions
|
||||
*
|
||||
* @param DoliDB $db Database handler
|
||||
*/
|
||||
public function __construct($db)
|
||||
{
|
||||
global $langs,$conf;
|
||||
|
||||
$this->db = $db;
|
||||
|
||||
// Id for module (must be unique).
|
||||
// Use here a free id (See in Home -> System information -> Dolibarr for list of used modules id).
|
||||
$this->numero = 610;
|
||||
// Key text used to identify module (for permissions, menus, etc...)
|
||||
$this->rights_class = 'attributes';
|
||||
|
||||
// Family can be 'crm','financial','hr','projects','products','ecm','technic','other'
|
||||
// It is used to group modules in module setup page
|
||||
$this->family = "products";
|
||||
// Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
|
||||
$this->name = preg_replace('/^mod/i','',get_class($this));
|
||||
// Module description, used if translation string 'ModuleXXXDesc' not found (where XXX is value of numeric property 'numero' of module)
|
||||
$this->description = 'Allows creating product combinations based on product attributes';
|
||||
// Possible values for version are: 'development', 'experimental', 'dolibarr' or version
|
||||
$this->version = 'dolibarr';
|
||||
// Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase)
|
||||
$this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
|
||||
// Where to store the module in setup page (0=common,1=interface,2=others,3=very specific)
|
||||
$this->special = 0;
|
||||
// Name of image file used for this module.
|
||||
// If file is in theme/yourtheme/img directory under name object_pictovalue.png, use this->picto='pictovalue'
|
||||
// If file is in module/img directory under name object_pictovalue.png, use this->picto='pictovalue@module'
|
||||
$this->picto='product';
|
||||
|
||||
// Defined all module parts (triggers, login, substitutions, menus, css, etc...)
|
||||
$this->module_parts = array();
|
||||
|
||||
// Data directories to create when module is enabled.
|
||||
// Example: this->dirs = array("/mymodule/temp");
|
||||
$this->dirs = array();
|
||||
|
||||
// Config pages. Put here list of php page, stored into mymodule/admin directory, to use to setup module.
|
||||
$this->config_page_url = array(
|
||||
'admin.php@attributes'
|
||||
);
|
||||
|
||||
// Dependencies
|
||||
$this->hidden = false; // A condition to hide module
|
||||
$this->depends = array(
|
||||
'modProduct'
|
||||
); // List of modules id that must be enabled if this module is enabled
|
||||
$this->requiredby = array(); // List of modules id to disable if this one is disabled
|
||||
$this->conflictwith = array(); // List of modules id this module is in conflict with
|
||||
$this->phpmin = array(5,0); // Minimum version of PHP required by module
|
||||
$this->need_dolibarr_version = array(3,0); // Minimum version of Dolibarr required by module
|
||||
$this->langfiles = array("products");
|
||||
|
||||
// Constants
|
||||
$this->const = array();
|
||||
|
||||
// Array to add new pages in new tabs
|
||||
$this->tabs = array(
|
||||
// 'product:+combinations:Combinaciones:products:1:/attributes/combinations.php?id=__ID__'
|
||||
);
|
||||
|
||||
// Dictionaries
|
||||
if (! isset($conf->mymodule->enabled))
|
||||
{
|
||||
$conf->mymodule=new stdClass();
|
||||
$conf->mymodule->enabled=0;
|
||||
}
|
||||
$this->dictionaries=array();
|
||||
|
||||
// Boxes
|
||||
// Add here list of php file(s) stored in core/boxes that contains class to show a box.
|
||||
$this->boxes = array(); // List of boxes
|
||||
|
||||
// Permissions
|
||||
$this->rights = array(); // Permission array used by this module
|
||||
|
||||
// Main menu entries
|
||||
$this->menu = array(
|
||||
array(
|
||||
'fk_menu' => 'fk_mainmenu=products,fk_leftmenu=product',
|
||||
'type' => 'left',
|
||||
'titre' => $langs->trans('Attributes'),
|
||||
'mainmenu' => 'products',
|
||||
'leftmenu' => 'product',
|
||||
'url' => '/attributes/list.php',
|
||||
'langs' => 'products',
|
||||
'position' => 100,
|
||||
'enabled' => '$conf->product->enabled',
|
||||
'perms' => 1,
|
||||
'target' => '',
|
||||
'user' => 0
|
||||
)
|
||||
); // List of menus to add
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
* Copyright (C) 2012 Cédric Salvador <csalvador@gpcsolutions.fr>
|
||||
* Copyright (C) 2014 Florian Henry <florian.henry@open-concept.pro>
|
||||
* Copyright (C) 2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
|
||||
* Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2015-2016 Marcos García <marcosgdf@gmail.com>
|
||||
*
|
||||
* 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
|
||||
@ -52,11 +52,12 @@ if (in_array($object->element,array('propal', 'supplier_proposal','facture','fac
|
||||
?>
|
||||
|
||||
<!-- BEGIN PHP TEMPLATE objectline_create.tpl.php -->
|
||||
<?php
|
||||
<?php
|
||||
$nolinesbefore=(count($this->lines) == 0);
|
||||
if ($nolinesbefore) {
|
||||
?>
|
||||
<tr class="liste_titre<?php echo (($nolinesbefore || $object->element=='contrat')?'':' liste_titre_add') ?> nodrag nodrop">
|
||||
<tr id="addline" class="liste_titre liste_titre_add nodrag nodrop">
|
||||
<td class="linecoldescription" <?php echo (! empty($conf->global->MAIN_VIEW_LINE_NUMBER) ? ' colspan="2"' : ''); ?>>
|
||||
<div id="add"></div><span class="hideonsmartphone"><?php echo $langs->trans('AddNewLine'); ?></span><?php // echo $langs->trans("FreeZone"); ?>
|
||||
</td>
|
||||
@ -94,7 +95,7 @@ if ($nolinesbefore) {
|
||||
<?php
|
||||
}
|
||||
else $colspan++;
|
||||
|
||||
|
||||
if ($conf->global->MARGIN_TYPE == "1")
|
||||
echo $langs->trans('BuyingPrice');
|
||||
else
|
||||
@ -108,7 +109,7 @@ if ($nolinesbefore) {
|
||||
?>
|
||||
<td class="linecoledit" colspan="<?php echo $colspan; ?>"> </td>
|
||||
</tr>
|
||||
<?php
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<tr class="pair nodrag nodrop nohoverpair<?php echo ($nolinesbefore || $object->element=='contrat')?'':' liste_titre_add'; ?>">
|
||||
@ -191,11 +192,11 @@ else {
|
||||
if ($conf->global->ENTREPOT_EXTRA_STATUS)
|
||||
{
|
||||
// hide products in closed warehouse, but show products for internal transfer
|
||||
$form->select_produits(GETPOST('idprod'), 'idprod', $filtertype, $conf->product->limit_size, $buyer->price_level, 1, 2, '', 1, array(),$buyer->id, '1', 0, '', 0, 'warehouseopen,warehouseinternal');
|
||||
$form->select_produits(GETPOST('idprod'), 'idprod', $filtertype, $conf->product->limit_size, $buyer->price_level, 1, 2, '', 1, array(),$buyer->id, '1', 0, '', 0, 'warehouseopen,warehouseinternal', GETPOST('combinations', 'array'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$form->select_produits(GETPOST('idprod'), 'idprod', $filtertype, $conf->product->limit_size, $buyer->price_level, 1, 2, '', 1, array(),$buyer->id);
|
||||
$form->select_produits(GETPOST('idprod'), 'idprod', $filtertype, $conf->product->limit_size, $buyer->price_level, 1, 2, '', 1, array(),$buyer->id, '1', 0, '', 0, '', GETPOST('combinations', 'array'));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -209,7 +210,7 @@ else {
|
||||
);
|
||||
$alsoproductwithnosupplierprice=0;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
$ajaxoptions = array();
|
||||
$alsoproductwithnosupplierprice=1;
|
||||
@ -237,7 +238,14 @@ else {
|
||||
}
|
||||
|
||||
|
||||
if (! empty($conf->product->enabled) || ! empty($conf->service->enabled)) echo '<br>';
|
||||
if (! empty($conf->product->enabled) || ! empty($conf->service->enabled)) {
|
||||
|
||||
if (!empty($conf->attributes->enabled)) {
|
||||
echo '<div id="attributes_box"></div>';
|
||||
}
|
||||
|
||||
echo '<br>';
|
||||
}
|
||||
|
||||
// Editor wysiwyg
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
|
||||
@ -306,7 +314,7 @@ else {
|
||||
<?php
|
||||
$coldisplay++;
|
||||
}
|
||||
|
||||
|
||||
if ($user->rights->margins->creer)
|
||||
{
|
||||
if (! empty($conf->global->DISPLAY_MARGIN_RATES))
|
||||
@ -561,9 +569,9 @@ jQuery(document).ready(function() {
|
||||
$("#idprod, #idprodfournprice").change(function()
|
||||
{
|
||||
console.log("#idprod, #idprodfournprice change triggered");
|
||||
|
||||
|
||||
setforpredef(); // TODO Keep vat combo visible and set it to first entry into list that match result of get_default_tva
|
||||
|
||||
|
||||
jQuery('#trlinefordates').show();
|
||||
|
||||
<?php
|
||||
@ -584,13 +592,13 @@ jQuery(document).ready(function() {
|
||||
var defaultkey = '';
|
||||
var defaultprice = '';
|
||||
var bestpricefound = 0;
|
||||
|
||||
|
||||
var bestpriceid = 0; var bestpricevalue = 0;
|
||||
var pmppriceid = 0; var pmppricevalue = 0;
|
||||
var costpriceid = 0; var costpricevalue = 0;
|
||||
|
||||
|
||||
/* setup of margin calculation */
|
||||
var defaultbuyprice = '<?php
|
||||
var defaultbuyprice = '<?php
|
||||
if (isset($conf->global->MARGIN_TYPE))
|
||||
{
|
||||
if ($conf->global->MARGIN_TYPE == '1') print 'bestsupplierprice';
|
||||
@ -598,7 +606,7 @@ jQuery(document).ready(function() {
|
||||
if ($conf->global->MARGIN_TYPE == 'costprice') print 'costprice';
|
||||
} ?>';
|
||||
console.log("we will set the field for margin. defaultbuyprice="+defaultbuyprice);
|
||||
|
||||
|
||||
var i = 0;
|
||||
$(data).each(function() {
|
||||
if (this.id != 'pmpprice' && this.id != 'costprice')
|
||||
@ -615,7 +623,7 @@ jQuery(document).ready(function() {
|
||||
//console.log("id="+this.id+"-price="+this.price);
|
||||
if ('pmp' == defaultbuyprice || 'costprice' == defaultbuyprice)
|
||||
{
|
||||
if (this.price > 0) {
|
||||
if (this.price > 0) {
|
||||
defaultkey = this.id; defaultprice = this.price; pmppriceid = this.id; pmppricevalue = this.price;
|
||||
//console.log("pmppricevalue="+pmppricevalue);
|
||||
}
|
||||
@ -634,22 +642,22 @@ jQuery(document).ready(function() {
|
||||
options += '<option value="'+this.id+'" price="'+this.price+'">'+this.label+'</option>';
|
||||
});
|
||||
options += '<option value="inputprice" price="'+defaultprice+'"><?php echo $langs->trans("InputPrice"); ?></option>';
|
||||
|
||||
|
||||
console.log("finally selected defaultkey="+defaultkey+" defaultprice="+defaultprice);
|
||||
|
||||
|
||||
$("#fournprice_predef").html(options).show();
|
||||
if (defaultkey != '')
|
||||
{
|
||||
$("#fournprice_predef").val(defaultkey);
|
||||
}
|
||||
|
||||
|
||||
/* At loading, no product are yet selected, so we hide field of buying_price */
|
||||
$("#buying_price").hide();
|
||||
|
||||
|
||||
/* Define default price at loading */
|
||||
var defaultprice = $("#fournprice_predef").find('option:selected').attr("price");
|
||||
$("#buying_price").val(defaultprice);
|
||||
|
||||
|
||||
$("#fournprice_predef").change(function() {
|
||||
console.log("change on fournprice_predef");
|
||||
/* Hide field buying_price according to choice into list (if 'inputprice' or not) */
|
||||
@ -701,8 +709,8 @@ function setforfree() {
|
||||
jQuery("#idprod").val('');
|
||||
jQuery("#idprodfournprice").val('0'); // Set cursor on not selected product
|
||||
jQuery("#search_idprodfournprice").val('');
|
||||
jQuery("#prod_entry_mode_free").prop('checked',true);
|
||||
jQuery("#prod_entry_mode_predef").prop('checked',false);
|
||||
jQuery("#prod_entry_mode_free").prop('checked',true).change();
|
||||
jQuery("#prod_entry_mode_predef").prop('checked',false).change();
|
||||
jQuery("#price_ht").show();
|
||||
jQuery("#multicurrency_price_ht").show();
|
||||
jQuery("#price_ttc").show(); // May no exists
|
||||
@ -721,8 +729,8 @@ function setforfree() {
|
||||
function setforpredef() {
|
||||
console.log("Call setforpredef. We hide some fields");
|
||||
jQuery("#select_type").val(-1);
|
||||
jQuery("#prod_entry_mode_free").prop('checked',false);
|
||||
jQuery("#prod_entry_mode_predef").prop('checked',true);
|
||||
jQuery("#prod_entry_mode_free").prop('checked',false).change();
|
||||
jQuery("#prod_entry_mode_predef").prop('checked',true).change();
|
||||
jQuery("#price_ht").hide();
|
||||
jQuery("#multicurrency_price_ht").hide();
|
||||
jQuery("#price_ttc").hide(); // May no exists
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
* Copyright (C) 2005-2016 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2010-2015 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2011-2015 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
|
||||
* Copyright (C) 2014 Ion Agorria <ion@agorria.com>
|
||||
*
|
||||
@ -50,6 +50,10 @@ if (!empty($conf->projet->enabled)) {
|
||||
}
|
||||
require_once NUSOAP_PATH.'/nusoap.php'; // Include SOAP
|
||||
|
||||
if (!empty($conf->attributes->enabled)) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
}
|
||||
|
||||
$langs->load('admin');
|
||||
$langs->load('orders');
|
||||
$langs->load('sendings');
|
||||
@ -284,7 +288,7 @@ if (empty($reshook))
|
||||
$product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):'');
|
||||
$date_start=dol_mktime(GETPOST('date_start'.$predef.'hour'), GETPOST('date_start'.$predef.'min'), GETPOST('date_start' . $predef . 'sec'), GETPOST('date_start'.$predef.'month'), GETPOST('date_start'.$predef.'day'), GETPOST('date_start'.$predef.'year'));
|
||||
$date_end=dol_mktime(GETPOST('date_end'.$predef.'hour'), GETPOST('date_end'.$predef.'min'), GETPOST('date_end' . $predef . 'sec'), GETPOST('date_end'.$predef.'month'), GETPOST('date_end'.$predef.'day'), GETPOST('date_end'.$predef.'year'));
|
||||
if (GETPOST('prod_entry_mode') == 'free')
|
||||
if ($prod_entry_mode == 'free')
|
||||
{
|
||||
$idprod=0;
|
||||
$price_ht = GETPOST('price_ht');
|
||||
@ -313,22 +317,22 @@ if (empty($reshook))
|
||||
}
|
||||
}
|
||||
|
||||
if (GETPOST('prod_entry_mode')=='free' && GETPOST('price_ht') < 0 && $qty < 0)
|
||||
if ($prod_entry_mode =='free' && GETPOST('price_ht') < 0 && $qty < 0)
|
||||
{
|
||||
setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPrice'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode')=='free' && ! GETPOST('idprodfournprice') && GETPOST('type') < 0)
|
||||
if ($prod_entry_mode =='free' && ! GETPOST('idprodfournprice') && GETPOST('type') < 0)
|
||||
{
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode')=='free' && GETPOST('price_ht')==='' && GETPOST('price_ttc')==='' && $price_ht_devise === '') // Unit price can be 0 but not ''
|
||||
if ($prod_entry_mode =='free' && GETPOST('price_ht')==='' && GETPOST('price_ttc')==='' && $price_ht_devise === '') // Unit price can be 0 but not ''
|
||||
{
|
||||
setEventMessages($langs->trans($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('UnitPrice'))), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode')=='free' && ! GETPOST('dp_desc'))
|
||||
if ($prod_entry_mode =='free' && ! GETPOST('dp_desc'))
|
||||
{
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
|
||||
$error++;
|
||||
@ -339,10 +343,24 @@ if (empty($reshook))
|
||||
$error++;
|
||||
}
|
||||
|
||||
// Ecrase $pu par celui du produit
|
||||
if (!$error && !empty($conf->attributes->enabled) && $prod_entry_mode != 'free') {
|
||||
if ($combinations = GETPOST('combinations', 'array')) {
|
||||
//Check if there is a product with the given combination
|
||||
$prodcomb = new ProductCombination($db);
|
||||
|
||||
if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
|
||||
$idprod = $res->fk_product_child;
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorProductCombinationNotFound'), 'errors');
|
||||
$error ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ecrase $pu par celui du produit
|
||||
// Ecrase $desc par celui du produit
|
||||
// Ecrase $txtva par celui du produit
|
||||
if ((GETPOST('prod_entry_mode') != 'free') && empty($error)) // With combolist mode idprodfournprice is > 0 or -1. With autocomplete, idprodfournprice is > 0 or ''
|
||||
if (($prod_entry_mode != 'free') && empty($error)) // With combolist mode idprodfournprice is > 0 or -1. With autocomplete, idprodfournprice is > 0 or ''
|
||||
{
|
||||
$productsupplier = new ProductFournisseur($db);
|
||||
|
||||
@ -441,7 +459,7 @@ if (empty($reshook))
|
||||
$ht = $ttc / (1 + ($tva_tx / 100));
|
||||
$price_base_type = 'HT';
|
||||
}
|
||||
|
||||
|
||||
$pu_ht_devise = price2num($price_ht_devise, 'MU');
|
||||
|
||||
$result=$object->addline($desc, $ht, $qty, $tva_tx, $localtax1_tx, $localtax2_tx, 0, 0, '', $remise_percent, $price_base_type, $ttc, $type,'','', $date_start, $date_end, $array_options, $fk_unit, $pu_ht_devise);
|
||||
@ -543,7 +561,7 @@ if (empty($reshook))
|
||||
|
||||
$localtax1_tx=get_localtax($tva_tx,1,$mysoc,$object->thirdparty);
|
||||
$localtax2_tx=get_localtax($tva_tx,2,$mysoc,$object->thirdparty);
|
||||
|
||||
|
||||
$pu_ht_devise = GETPOST('multicurrency_subprice');
|
||||
|
||||
// Extrafields Lines
|
||||
@ -1416,7 +1434,7 @@ if ($action=='create')
|
||||
// If not defined, set default value from constant
|
||||
if (empty($cond_reglement_id) && ! empty($conf->global->SUPPLIER_ORDER_DEFAULT_PAYMENT_TERM_ID)) $cond_reglement_id=$conf->global->SUPPLIER_ORDER_DEFAULT_PAYMENT_TERM_ID;
|
||||
if (empty($mode_reglement_id) && ! empty($conf->global->SUPPLIER_ORDER_DEFAULT_PAYMENT_MODE_ID)) $mode_reglement_id=$conf->global->SUPPLIER_ORDER_DEFAULT_PAYMENT_MODE_ID;
|
||||
|
||||
|
||||
print '<form name="add" action="'.$_SERVER["PHP_SELF"].'" method="post">';
|
||||
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
|
||||
print '<input type="hidden" name="action" value="add">';
|
||||
@ -1615,7 +1633,7 @@ if ($action=='create')
|
||||
elseif (! empty($object->id))
|
||||
{
|
||||
$result = $object->fetch($id, $ref);
|
||||
|
||||
|
||||
$societe = new Fournisseur($db);
|
||||
$result=$societe->fetch($object->socid);
|
||||
if ($result < 0) dol_print_error($db);
|
||||
@ -2637,7 +2655,7 @@ elseif (! empty($object->id))
|
||||
print '<div class="inline-block divButAction"><a class="butActionRefused" href="#">'.$langs->trans("MakeOrder").'</a></div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create bill
|
||||
if (! empty($conf->facture->enabled))
|
||||
{
|
||||
@ -2703,11 +2721,11 @@ elseif (! empty($object->id))
|
||||
$date_com = dol_mktime(0, 0, 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear'));
|
||||
print $form->select_date($date_com,'',1,1,'',"commande",1,0,1);
|
||||
print '</td></tr>';
|
||||
|
||||
|
||||
print '<tr><td>'.$langs->trans("OrderMode").'</td><td>';
|
||||
$formorder->selectInputMethod(GETPOST('methodecommande'), "methodecommande", 1);
|
||||
print '</td></tr>';
|
||||
|
||||
|
||||
print '<tr><td>'.$langs->trans("Comment").'</td><td><input size="40" type="text" name="comment" value="'.GETPOST('comment').'"></td></tr>';
|
||||
print '<tr><td align="center" colspan="2">';
|
||||
print '<input type="submit" name="makeorder" class="button" value="'.$langs->trans("ToOrder").'">';
|
||||
@ -2718,12 +2736,10 @@ elseif (! empty($object->id))
|
||||
print '</form>';
|
||||
print "<br>";
|
||||
}
|
||||
|
||||
|
||||
if ($action != 'makeorder')
|
||||
if ($action != 'makeorder')
|
||||
{
|
||||
print '<div class="fichecenter"><div class="fichehalfleft">';
|
||||
|
||||
|
||||
/*
|
||||
* Documents generes
|
||||
*/
|
||||
@ -2734,57 +2750,57 @@ elseif (! empty($object->id))
|
||||
$urlsource=$_SERVER["PHP_SELF"]."?id=".$object->id;
|
||||
$genallowed=$user->rights->fournisseur->commande->creer;
|
||||
$delallowed=$user->rights->fournisseur->commande->supprimer;
|
||||
|
||||
|
||||
print $formfile->showdocuments('commande_fournisseur',$comfournref,$filedir,$urlsource,$genallowed,$delallowed,$object->modelpdf,1,0,0,0,0,'','','',$object->thirdparty->default_lang);
|
||||
$somethingshown=$formfile->numoffiles;
|
||||
|
||||
|
||||
// Show links to link elements
|
||||
$linktoelem = $form->showLinkToObjectBlock($object, null, array('supplier_order','order_supplier'));
|
||||
$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
|
||||
|
||||
|
||||
print '</div><div class="fichehalfright"><div class="ficheaddleft">';
|
||||
|
||||
|
||||
if ($user->rights->fournisseur->commande->receptionner && ($object->statut == 3 || $object->statut == 4))
|
||||
{
|
||||
// Set status to received (action=livraison)
|
||||
print '<!-- form to record supplier order received -->'."\n";
|
||||
print '<form action="card.php?id='.$object->id.'" method="post">';
|
||||
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
|
||||
print '<input type="hidden" name="action" value="livraison">';
|
||||
print load_fiche_titre($langs->trans("Receive"),'','');
|
||||
|
||||
print '<table class="noborder" width="100%">';
|
||||
//print '<tr class="liste_titre"><td colspan="2">'.$langs->trans("Receive").'</td></tr>';
|
||||
print '<tr><td>'.$langs->trans("DeliveryDate").'</td><td>';
|
||||
print $form->select_date('','',1,1,'',"commande",1,0,1);
|
||||
print "</td></tr>\n";
|
||||
|
||||
print "<tr><td>".$langs->trans("Delivery")."</td><td>\n";
|
||||
$liv = array();
|
||||
$liv[''] = ' ';
|
||||
$liv['tot'] = $langs->trans("CompleteOrNoMoreReceptionExpected");
|
||||
$liv['par'] = $langs->trans("PartialWoman");
|
||||
$liv['nev'] = $langs->trans("NeverReceived");
|
||||
$liv['can'] = $langs->trans("Canceled");
|
||||
|
||||
print $form->selectarray("type",$liv);
|
||||
|
||||
print '</td></tr>';
|
||||
print '<tr><td>'.$langs->trans("Comment").'</td><td><input size="40" type="text" name="comment"></td></tr>';
|
||||
print '<tr><td align="center" colspan="2"><input type="submit" class="button" value="'.$langs->trans("Receive").'"></td></tr>';
|
||||
print "</table>\n";
|
||||
print "</form>\n";
|
||||
print "<br>";
|
||||
}
|
||||
|
||||
// List of actions on element
|
||||
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
|
||||
$formactions=new FormActions($db);
|
||||
$somethingshown=$formactions->showactions($object,'order_supplier',$socid,0,'listaction'.($genallowed?'largetitle':''));
|
||||
|
||||
print '</div></div></div>';
|
||||
if ($user->rights->fournisseur->commande->receptionner && ($object->statut == 3 || $object->statut == 4))
|
||||
{
|
||||
// Set status to received (action=livraison)
|
||||
print '<!-- form to record supplier order received -->'."\n";
|
||||
print '<form action="card.php?id='.$object->id.'" method="post">';
|
||||
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
|
||||
print '<input type="hidden" name="action" value="livraison">';
|
||||
print load_fiche_titre($langs->trans("Receive"),'','');
|
||||
|
||||
print '<table class="noborder" width="100%">';
|
||||
//print '<tr class="liste_titre"><td colspan="2">'.$langs->trans("Receive").'</td></tr>';
|
||||
print '<tr><td>'.$langs->trans("DeliveryDate").'</td><td>';
|
||||
print $form->select_date('','',1,1,'',"commande",1,0,1);
|
||||
print "</td></tr>\n";
|
||||
|
||||
print "<tr><td>".$langs->trans("Delivery")."</td><td>\n";
|
||||
$liv = array();
|
||||
$liv[''] = ' ';
|
||||
$liv['tot'] = $langs->trans("CompleteOrNoMoreReceptionExpected");
|
||||
$liv['par'] = $langs->trans("PartialWoman");
|
||||
$liv['nev'] = $langs->trans("NeverReceived");
|
||||
$liv['can'] = $langs->trans("Canceled");
|
||||
|
||||
print $form->selectarray("type",$liv);
|
||||
|
||||
print '</td></tr>';
|
||||
print '<tr><td>'.$langs->trans("Comment").'</td><td><input size="40" type="text" name="comment"></td></tr>';
|
||||
print '<tr><td align="center" colspan="2"><input type="submit" class="button" value="'.$langs->trans("Receive").'"></td></tr>';
|
||||
print "</table>\n";
|
||||
print "</form>\n";
|
||||
print "<br>";
|
||||
}
|
||||
|
||||
// List of actions on element
|
||||
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
|
||||
$formactions=new FormActions($db);
|
||||
$somethingshown=$formactions->showactions($object,'order_supplier',$socid,0,'listaction'.($genallowed?'largetitle':''));
|
||||
|
||||
|
||||
|
||||
|
||||
print '</div></div></div>';}
|
||||
}
|
||||
|
||||
print '</td></tr></table>';
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Copyright (C) 2010-2014 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2013-2015 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
|
||||
* Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2016 Alexandre Spangaro <aspangaro@zendsi.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -46,6 +46,10 @@ if (!empty($conf->projet->enabled)) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
|
||||
}
|
||||
|
||||
if (!empty($conf->attributes->enabled)) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
}
|
||||
|
||||
|
||||
$langs->load('bills');
|
||||
$langs->load('compta');
|
||||
@ -321,7 +325,7 @@ if (empty($reshook))
|
||||
if (empty($conf->global->INVOICE_MAX_OFFSET_IN_FUTURE)) setEventMessages($langs->trans("WarningInvoiceDateInFuture"), null, 'warnings');
|
||||
else setEventMessages($langs->trans("WarningInvoiceDateTooFarInFuture"), null, 'warnings');
|
||||
}
|
||||
|
||||
|
||||
$object->fetch($id);
|
||||
|
||||
$object->date=$newdate;
|
||||
@ -878,7 +882,8 @@ if (empty($reshook))
|
||||
// Set if we used free entry or predefined product
|
||||
$predef='';
|
||||
$product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):'');
|
||||
if (GETPOST('prod_entry_mode') == 'free')
|
||||
$prod_entry_mode = GETPOST('prod_entry_mode');
|
||||
if ($prod_entry_mode == 'free')
|
||||
{
|
||||
$idprod=0;
|
||||
$price_ht = GETPOST('price_ht');
|
||||
@ -910,22 +915,22 @@ if (empty($reshook))
|
||||
}
|
||||
}
|
||||
|
||||
if (GETPOST('prod_entry_mode')=='free' && GETPOST('price_ht') < 0 && $qty < 0)
|
||||
if ($prod_entry_mode =='free' && GETPOST('price_ht') < 0 && $qty < 0)
|
||||
{
|
||||
setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPrice'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode')=='free' && ! GETPOST('idprodfournprice') && GETPOST('type') < 0)
|
||||
if ($prod_entry_mode =='free' && ! GETPOST('idprodfournprice') && GETPOST('type') < 0)
|
||||
{
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode')=='free' && GETPOST('price_ht')==='' && GETPOST('price_ttc')==='' && $price_ht_devise==='') // Unit price can be 0 but not ''
|
||||
if ($prod_entry_mode =='free' && GETPOST('price_ht')==='' && GETPOST('price_ttc')==='' && $price_ht_devise==='') // Unit price can be 0 but not ''
|
||||
{
|
||||
setEventMessages($langs->trans($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('UnitPrice'))), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode')=='free' && ! GETPOST('dp_desc'))
|
||||
if ($prod_entry_mode =='free' && ! GETPOST('dp_desc'))
|
||||
{
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
|
||||
$error++;
|
||||
@ -936,7 +941,21 @@ if (empty($reshook))
|
||||
$error++;
|
||||
}
|
||||
|
||||
if (GETPOST('prod_entry_mode') != 'free' && empty($error)) // With combolist mode idprodfournprice is > 0 or -1. With autocomplete, idprodfournprice is > 0 or ''
|
||||
if (!$error && !empty($conf->attributes->enabled) && $prod_entry_mode != 'free') {
|
||||
if ($combinations = GETPOST('combinations', 'array')) {
|
||||
//Check if there is a product with the given combination
|
||||
$prodcomb = new ProductCombination($db);
|
||||
|
||||
if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
|
||||
$idprod = $res->fk_product_child;
|
||||
} else {
|
||||
setEventMessage($langs->trans('ErrorProductCombinationNotFound'), 'errors');
|
||||
$error ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($prod_entry_mode != 'free' && empty($error)) // With combolist mode idprodfournprice is > 0 or -1. With autocomplete, idprodfournprice is > 0 or ''
|
||||
{
|
||||
$idprod=0;
|
||||
$productsupplier=new ProductFournisseur($db);
|
||||
@ -2297,9 +2316,9 @@ else
|
||||
/*
|
||||
* List of payments
|
||||
*/
|
||||
|
||||
|
||||
$totalpaye = 0;
|
||||
|
||||
|
||||
$sign = 1;
|
||||
if ($object->type == FactureFournisseur::TYPE_CREDIT_NOTE) $sign = - 1;
|
||||
|
||||
@ -2407,7 +2426,7 @@ else
|
||||
dol_print_error($db);
|
||||
}
|
||||
|
||||
if ($object->type != FactureFournisseur::TYPE_CREDIT_NOTE)
|
||||
if ($object->type != FactureFournisseur::TYPE_CREDIT_NOTE)
|
||||
{
|
||||
// Total already paid
|
||||
print '<tr><td colspan="' . $nbcols . '" align="right">';
|
||||
@ -2457,7 +2476,7 @@ else
|
||||
dol_print_error($db);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// Paye partiellement 'escompte'
|
||||
if (($object->statut == FactureFournisseur::STATUS_CLOSED || $object->statut == FactureFournisseur::STATUS_ABANDONED) && $object->close_code == 'discount_vat') {
|
||||
print '<tr><td colspan="' . $nbcols . '" align="right" class="nowrap">';
|
||||
|
||||
@ -65,7 +65,7 @@ ALTER TABLE llx_product_lot ADD UNIQUE INDEX uk_product_lot(fk_product, batch);
|
||||
ALTER TABLE llx_product_lot MODIFY COLUMN entity integer DEFAULT 1;
|
||||
UPDATE llx_product_lot SET entity = 1 WHERE entity IS NULL;
|
||||
|
||||
DROP TABLE llx_stock_serial;
|
||||
DROP TABLE llx_stock_serial;
|
||||
|
||||
ALTER TABLE llx_product ADD COLUMN note_public text;
|
||||
ALTER TABLE llx_user ADD COLUMN note_public text;
|
||||
|
||||
@ -250,3 +250,40 @@ ALTER TABLE llx_projet_task ADD UNIQUE INDEX uk_projet_task_ref (ref, entity);
|
||||
|
||||
ALTER TABLE llx_contrat ADD COLUMN fk_user_modif integer;
|
||||
|
||||
|
||||
-- Product attributes
|
||||
CREATE TABLE llx_product_attribute
|
||||
(
|
||||
rowid INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
ref VARCHAR(255) NOT NULL,
|
||||
label VARCHAR(255) NOT NULL,
|
||||
rang INT DEFAULT 0 NOT NULL,
|
||||
entity INT DEFAULT 1 NOT NULL
|
||||
);
|
||||
ALTER TABLE llx_product_attribute ADD CONSTRAINT unique_ref UNIQUE (ref);
|
||||
CREATE TABLE llx_product_attribute_combination
|
||||
(
|
||||
rowid INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
fk_product_parent INT NOT NULL,
|
||||
fk_product_child INT NOT NULL,
|
||||
variation_price FLOAT NOT NULL,
|
||||
variation_price_percentage INT NULL,
|
||||
variation_weight FLOAT NOT NULL,
|
||||
entity INT DEFAULT 1 NOT NULL
|
||||
);
|
||||
CREATE TABLE llx_product_attribute_combination2val
|
||||
(
|
||||
rowid INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
fk_prod_combination INT NOT NULL,
|
||||
fk_prod_attr INT NOT NULL,
|
||||
fk_prod_attr_val INT NOT NULL
|
||||
);
|
||||
CREATE TABLE llx_product_attribute_value
|
||||
(
|
||||
rowid INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
fk_product_attribute INT NOT NULL,
|
||||
ref VARCHAR(255) DEFAULT NULL,
|
||||
value VARCHAR(255) DEFAULT NULL,
|
||||
entity INT DEFAULT 1 NOT NULL
|
||||
);
|
||||
ALTER TABLE llx_product_attribute_value ADD CONSTRAINT unique_ref UNIQUE (fk_product_attribute,ref);
|
||||
19
htdocs/install/mysql/tables/llx_product_attribute.key.sql
Normal file
19
htdocs/install/mysql/tables/llx_product_attribute.key.sql
Normal file
@ -0,0 +1,19 @@
|
||||
-- ============================================================================
|
||||
-- Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation; either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
--
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE llx_product_attribute ADD CONSTRAINT unique_ref UNIQUE (ref);
|
||||
26
htdocs/install/mysql/tables/llx_product_attribute.sql
Normal file
26
htdocs/install/mysql/tables/llx_product_attribute.sql
Normal file
@ -0,0 +1,26 @@
|
||||
-- ============================================================================
|
||||
-- Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation; either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
--
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE llx_product_attribute
|
||||
(
|
||||
rowid INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
ref VARCHAR(255) NOT NULL,
|
||||
label VARCHAR(255) NOT NULL,
|
||||
rang INT DEFAULT 0 NOT NULL,
|
||||
entity INT DEFAULT 1 NOT NULL
|
||||
);
|
||||
@ -0,0 +1,28 @@
|
||||
-- ============================================================================
|
||||
-- Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation; either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
--
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE llx_product_attribute_combination
|
||||
(
|
||||
rowid INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
fk_product_parent INT NOT NULL,
|
||||
fk_product_child INT NOT NULL,
|
||||
variation_price FLOAT NOT NULL,
|
||||
variation_price_percentage INT NULL,
|
||||
variation_weight FLOAT NOT NULL,
|
||||
entity INT DEFAULT 1 NOT NULL
|
||||
);
|
||||
@ -0,0 +1,25 @@
|
||||
-- ============================================================================
|
||||
-- Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation; either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
--
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE llx_product_attribute_combination2val
|
||||
(
|
||||
rowid INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
fk_prod_combination INT NOT NULL,
|
||||
fk_prod_attr INT NOT NULL,
|
||||
fk_prod_attr_val INT NOT NULL
|
||||
);
|
||||
@ -0,0 +1,19 @@
|
||||
-- ============================================================================
|
||||
-- Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation; either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
--
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE llx_product_attribute_value ADD CONSTRAINT unique_ref UNIQUE (fk_product_attribute,ref);
|
||||
26
htdocs/install/mysql/tables/llx_product_attribute_value.sql
Normal file
26
htdocs/install/mysql/tables/llx_product_attribute_value.sql
Normal file
@ -0,0 +1,26 @@
|
||||
-- ============================================================================
|
||||
-- Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation; either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
--
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE llx_product_attribute_value
|
||||
(
|
||||
rowid INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
fk_product_attribute INT NOT NULL,
|
||||
ref VARCHAR(255) DEFAULT NULL,
|
||||
value VARCHAR(255) DEFAULT NULL,
|
||||
entity INT DEFAULT 1 NOT NULL
|
||||
);
|
||||
@ -812,3 +812,5 @@ SearchIntoContracts=Contracts
|
||||
SearchIntoCustomerShipments=Customer shipments
|
||||
SearchIntoExpenseReports=Expense reports
|
||||
SearchIntoLeaves=Leaves
|
||||
|
||||
BulkActions=Bulk actions
|
||||
@ -142,6 +142,7 @@ ConfirmCloneProduct=Are you sure you want to clone product or service <b>%s</b>?
|
||||
CloneContentProduct=Clone all main informations of product/service
|
||||
ClonePricesProduct=Clone main informations and prices
|
||||
CloneCompositionProduct=Clone packaged product/service
|
||||
CloneCombinationsProduct=Clone product combinations
|
||||
ProductIsUsed=This product is used
|
||||
NewRefForClone=Ref. of new product/service
|
||||
SellingPrices=Selling prices
|
||||
@ -231,7 +232,7 @@ ComposedProduct=Sub-product
|
||||
MinSupplierPrice=Minimum supplier price
|
||||
MinCustomerPrice=Minimum customer price
|
||||
DynamicPriceConfiguration=Dynamic price configuration
|
||||
DynamicPriceDesc=On product card, with this module enabled, you should be able to set mathematic functions to calculate Customer or Supplier prices. Such function can use all mathematic operators, some constants and variables. You can set here the variables you want to be able and if the variable need an automatic update, the external URL to use to ask Dolibarr to update automaticaly the value.
|
||||
DynamicPriceDesc=On product card, with this module enabled, you should be able to set mathematic functions to calculate Customer or Supplier prices. Such function can use all mathematic operators, some constants and variables. You can set here the variables you want to be able and if the variable need an automatic update, the external URL to use to ask Dolibarr to update automaticaly the value.
|
||||
AddVariable=Add Variable
|
||||
AddUpdater=Add Updater
|
||||
GlobalVariables=Global variables
|
||||
@ -259,3 +260,38 @@ SizeUnits=Size unit
|
||||
DeleteProductBuyPrice=Delete buying price
|
||||
ConfirmDeleteProductBuyPrice=Are you sure you want to delete this buying price?
|
||||
SubProduct=Sub product
|
||||
|
||||
#Attributes
|
||||
ProductAttributes=Product attributes
|
||||
ProductAttributeName=Attribute %s
|
||||
ProductAttribute=Attribute
|
||||
ProductAttributeDeleteDialog=Are you sure you want to delete this attribute? All values will be deleted
|
||||
ProductAttributeValueDeleteDialog=Are you sure you want to delete the value "%s" with reference "%s" of this attribute?
|
||||
ProductCombinationDeleteDialog=Are you sure want to delete the combination of the product "%s"?
|
||||
ProductCombinationAlreadyUsed=There was an error while deleting the combination. Please check it is not being used in any object
|
||||
ProductCombinations=Combinations
|
||||
HideProductCombinations=Hide derived products from combinations in the product selector
|
||||
ProductCombination=Combination
|
||||
NewProductCombination=New combination
|
||||
EditProductCombination=Editing combination
|
||||
ProductCombinationGenerator=Combinations generator
|
||||
Features=Features
|
||||
PriceImpact=Price impact
|
||||
WeightImpact=Weight impact
|
||||
NewProductAttribute=New attribute
|
||||
NewProductAttributeValue=New attribute value
|
||||
ErrorCreatingProductAttributeValue=There was an error while creating the attribute value. It could be because there is already an existing value with that reference
|
||||
ProductCombinationGeneratorWarning=If you continue, before generating new combinations, all previous ones will be DELETED. Already existing ones will be updated with the new values
|
||||
TooMuchCombinationsWarning=Generating lots of combinations may result in high CPU, memory usage and Dolibarr not able to create them. Enabling the option "%s" may help reduce memory usage.
|
||||
DoNotRemovePreviousCombinations=Do not remove previous combinations
|
||||
UsePercentageVariations=Use percentage variations
|
||||
PercentageVariation=Percentage variation
|
||||
ErrorDeletingGeneratedProducts=There was an error while trying to delete existing product combinations
|
||||
NbProducts=No. of products
|
||||
ParentProduct=Parent product
|
||||
HideChildProducts=Hide child products
|
||||
ConfirmCloneProductCombinations=Would you like to copy all the product combinations to the product with the given reference?
|
||||
CloneDestinationReference=Destination product reference
|
||||
ErrorCopyProductCombinations=There was an error while copying the product combinations
|
||||
ErrorDestinationProductNotFound=Destination product not found
|
||||
ErrorProductCombinationNotFound=Product combination not found
|
||||
@ -6,7 +6,7 @@
|
||||
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
|
||||
* Copyright (C) 2006 Auguria SARL <info@auguria.org>
|
||||
* Copyright (C) 2010-2015 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2013-2014 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2013-2016 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2012-2013 Cédric Salvador <csalvador@gpcsolutions.fr>
|
||||
* Copyright (C) 2011-2016 Alexandre Spangaro <aspangaro.dolibarr@gmail.com>
|
||||
* Copyright (C) 2014 Cédric Gross <c.gross@kreiz-it.fr>
|
||||
@ -1603,6 +1603,22 @@ else
|
||||
print dol_print_url($object->url);
|
||||
print '</td></tr>';
|
||||
|
||||
//Parent product.
|
||||
if (!empty($conf->attributes->enabled) && $object->isProduct()) {
|
||||
|
||||
$combination = new ProductCombination($db);
|
||||
|
||||
if ($combination->fetchByFkProductChild($object->id) > 0) {
|
||||
$prodstatic = new Product($db);
|
||||
$prodstatic->fetch($combination->fk_product_parent);
|
||||
|
||||
// Parent product
|
||||
print '<tr><td>'.$langs->trans("ParentProduct").'</td><td colspan="2">';
|
||||
print $prodstatic->getNomUrl(1);
|
||||
print '</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
print '<div class="fichehalfright"><div class="ficheaddleft">';
|
||||
|
||||
@ -960,6 +960,17 @@ class Product extends CommonObject
|
||||
|
||||
if (! $error)
|
||||
{
|
||||
if ($conf->attributes->enabled) {
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
|
||||
$comb = new ProductCombination($this->db);
|
||||
|
||||
foreach ($comb->fetchAllByFkProductParent($this->id) as $currcomb) {
|
||||
$currcomb->updateProperties($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->commit();
|
||||
return 1;
|
||||
}
|
||||
@ -1081,6 +1092,26 @@ class Product extends CommonObject
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php';
|
||||
|
||||
//If it is a parent product, then we remove the association with child products
|
||||
$prodcomb = new ProductCombination($this->db);
|
||||
|
||||
if ($prodcomb->deleteByFkProductParent($id) < 0) {
|
||||
$error++;
|
||||
$this->errors[] = 'Error deleting combinations';
|
||||
}
|
||||
|
||||
//We also check if it is a child product
|
||||
if (!$error && ($prodcomb->fetchByFkProductChild($id) > 0) && ($prodcomb->delete() < 0)) {
|
||||
$error++;
|
||||
$this->errors[] = 'Error deleting child combination';
|
||||
}
|
||||
}
|
||||
|
||||
// Delete product
|
||||
if (! $error)
|
||||
{
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
/* Copyright (C) 2001-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
|
||||
* Copyright (C) 2004-2016 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2012-2013 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2013-2016 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2013-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
|
||||
* Copyright (C) 2013 Jean Heimburger <jean@tiaris.info>
|
||||
@ -50,7 +50,7 @@ $sref=GETPOST("sref");
|
||||
$sbarcode=GETPOST("sbarcode");
|
||||
$snom=GETPOST("snom");
|
||||
$sall=GETPOST("sall");
|
||||
$type=GETPOST("type","int");
|
||||
$type= (int) GETPOST("type","int");
|
||||
$search_sale = GETPOST("search_sale");
|
||||
$search_categ = GETPOST("search_categ",'int');
|
||||
$tosell = GETPOST("tosell", 'int');
|
||||
@ -62,6 +62,13 @@ $search_accountancy_code_sell = GETPOST("search_accountancy_code_sell",'alpha');
|
||||
$search_accountancy_code_buy = GETPOST("search_accountancy_code_buy",'alpha');
|
||||
$optioncss = GETPOST('optioncss','alpha');
|
||||
|
||||
//Show/hide child products. Hidden by default
|
||||
if (!$_POST) {
|
||||
$search_hidechildproducts = 'on';
|
||||
} else {
|
||||
$search_hidechildproducts = GETPOST('search_hidechildproducts');
|
||||
}
|
||||
|
||||
$limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit;
|
||||
$sortfield = GETPOST("sortfield",'alpha');
|
||||
$sortorder = GETPOST("sortorder",'alpha');
|
||||
@ -242,6 +249,9 @@ else
|
||||
$sql.= ' p.datec as date_creation, p.tms as date_update,';
|
||||
//$sql.= ' pfp.ref_fourn as ref_supplier, ';
|
||||
$sql.= ' MIN(pfp.unitprice) as minsellprice';
|
||||
if (!empty($conf->attributes->enabled) && $search_hidechildproducts && ($type === 0)) {
|
||||
$sql .= ', pac.rowid prod_comb_id';
|
||||
}
|
||||
// Add fields from extrafields
|
||||
foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key.' as options_'.$key : '');
|
||||
// Add fields from hooks
|
||||
@ -254,6 +264,10 @@ else
|
||||
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
|
||||
// multilang
|
||||
if (! empty($conf->global->MAIN_MULTILANGS)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid AND pl.lang = '".$langs->getDefaultLang() ."'";
|
||||
if (!empty($conf->attributes->enabled) && $search_hidechildproducts && ($type === 0)) {
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
|
||||
}
|
||||
|
||||
$sql.= ' WHERE p.entity IN ('.getEntity('product', 1).')';
|
||||
if ($sall) $sql .= natural_search(array_keys($fieldstosearchall), $sall);
|
||||
// if the type is not 1, we show all products (type = 0,2,3)
|
||||
@ -277,6 +291,12 @@ else
|
||||
if ($search_accountancy_code_sell) $sql.= natural_search('p.accountancy_code_sell', $search_accountancy_code_sell);
|
||||
if ($search_accountancy_code_sell) $sql.= natural_search('p.accountancy_code_buy', $search_accountancy_code_buy);
|
||||
// Add where from extra fields
|
||||
|
||||
if (!empty($conf->attributes->enabled) && $search_hidechildproducts && ($type === 0)) {
|
||||
$sql .= " AND pac.rowid IS NULL";
|
||||
}
|
||||
|
||||
// Add where from extra fields
|
||||
foreach ($search_array_options as $key => $val)
|
||||
{
|
||||
$crit=$val;
|
||||
@ -296,6 +316,9 @@ else
|
||||
$sql.= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,";
|
||||
$sql.= " p.fk_product_type, p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock,";
|
||||
$sql.= ' p.datec, p.tms, p.entity, p.tobatch, p.accountancy_code_sell, p.accountancy_code_buy';
|
||||
if (!empty($conf->attributes->enabled) && $search_hidechildproducts && ($type === 0)) {
|
||||
$sql .= ', pac.rowid';
|
||||
}
|
||||
// Add fields from extrafields
|
||||
foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key : '');
|
||||
// Add fields from hooks
|
||||
@ -426,6 +449,15 @@ else
|
||||
$moreforfilter.=$htmlother->select_categories(Categorie::TYPE_PRODUCT,$search_categ,'search_categ',1);
|
||||
$moreforfilter.='</div>';
|
||||
}
|
||||
|
||||
//Show/hide child products. Hidden by default
|
||||
if (!empty($conf->attributes->enabled) && $type === 0) {
|
||||
$moreforfilter.='<div class="divsearchfield">';
|
||||
$moreforfilter.= '<input type="checkbox" id="search_hidechildproducts" name="search_hidechildproducts" value="on"'.($search_hidechildproducts ? 'checked="checked"' : '').'>';
|
||||
$moreforfilter.= ' <label for="search_hidechildproducts">'.$langs->trans('HideChildProducts').'</label>';
|
||||
$moreforfilter.='</div>';
|
||||
}
|
||||
|
||||
if ($moreforfilter)
|
||||
{
|
||||
print '<div class="liste_titre liste_titre_bydiv centpercent">';
|
||||
@ -627,7 +659,7 @@ else
|
||||
$product_static->status_buy = $objp->tobuy;
|
||||
$product_static->status = $objp->tosell;
|
||||
$product_static->entity = $objp->entity;
|
||||
|
||||
|
||||
if (! empty($conf->stock->enabled) && $user->rights->stock->lire && $type != 1) // To optimize call of load_stock
|
||||
{
|
||||
if ($objp->fk_product_type != 1) // Not a service
|
||||
@ -635,8 +667,8 @@ else
|
||||
$product_static->load_stock('nobatch'); // Load stock_reel + stock_warehouse. This also call load_virtual_stock()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$var=!$var;
|
||||
print '<tr '.$bc[$var].'>';
|
||||
|
||||
@ -765,7 +797,7 @@ else
|
||||
print '<td align="center">';
|
||||
print yn($objp->tobatch);
|
||||
print '</td>';
|
||||
}
|
||||
}
|
||||
// Accountancy code sell
|
||||
if (! empty($arrayfields['p.accountancy_code_sell']['checked'])) print '<td>'.$objp->accountancy_code_sell.'</td>';
|
||||
// Accountancy code sell
|
||||
|
||||
Loading…
Reference in New Issue
Block a user