forked from Wavyzz/dolibarr
NEW Added product attributes feature
This commit is contained in:
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();
|
||||
317
htdocs/attributes/class/ProductAttribute.class.php
Normal file
317
htdocs/attributes/class/ProductAttribute.class.php
Normal file
@@ -0,0 +1,317 @@
|
||||
<?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
|
||||
{
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
221
htdocs/attributes/class/ProductAttributeValue.class.php
Normal file
221
htdocs/attributes/class/ProductAttributeValue.class.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?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
|
||||
{
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
641
htdocs/attributes/class/ProductCombination.class.php
Normal file
641
htdocs/attributes/class/ProductCombination.class.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/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
151
htdocs/attributes/class/ProductCombination2ValuePair.class.php
Normal file
151
htdocs/attributes/class/ProductCombination2ValuePair.class.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?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
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
@@ -48,6 +49,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');
|
||||
@@ -655,7 +660,8 @@ if (empty($reshook))
|
||||
$predef='';
|
||||
$product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):'');
|
||||
$price_ht = GETPOST('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);
|
||||
@@ -681,21 +687,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 == '') // Unit price can be 0 but not ''. Also price can be negative for proposal.
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && $price_ht == '') // 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;
|
||||
|
||||
@@ -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');
|
||||
@@ -621,7 +625,8 @@ if (empty($reshook))
|
||||
$predef='';
|
||||
$product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):'');
|
||||
$price_ht = GETPOST('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);
|
||||
@@ -651,11 +656,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 == '')) // Unit price can be 0 but not ''
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '')) // Unit price can be 0 but not ''
|
||||
{
|
||||
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
|
||||
$error++;
|
||||
@@ -664,11 +669,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'));
|
||||
|
||||
@@ -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');
|
||||
@@ -1322,7 +1326,8 @@ if (empty($reshook))
|
||||
$predef='';
|
||||
$product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):'');
|
||||
$price_ht = GETPOST('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);
|
||||
@@ -1352,11 +1357,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 == '')) // Unit price can be 0 but not ''
|
||||
if ($prod_entry_mode == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '')) // Unit price can be 0 but not ''
|
||||
{
|
||||
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
|
||||
$error ++;
|
||||
@@ -1365,7 +1370,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 ++;
|
||||
}
|
||||
@@ -1374,7 +1379,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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -1605,9 +1605,10 @@ class Form
|
||||
* @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after)
|
||||
* @param array $ajaxoptions Options for ajax_autocompleter
|
||||
* @param int $socid Thirdparty Id (to get also price dedicated to this customer)
|
||||
* @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)
|
||||
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, $selected_combinations = array())
|
||||
{
|
||||
global $langs,$conf;
|
||||
|
||||
@@ -1632,6 +1633,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,17 @@ 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 (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
|
||||
$sql .= " AND pac.rowid IS NULL";
|
||||
}
|
||||
|
||||
if ($finished == 0)
|
||||
{
|
||||
$sql.= " AND p.finished = ".$finished;
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
@@ -2058,4 +2058,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
|
||||
@@ -106,6 +106,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)
|
||||
|
||||
@@ -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,7 +52,7 @@ if (in_array($object->element,array('propal', 'supplier_proposal','facture','fac
|
||||
|
||||
<!-- BEGIN PHP TEMPLATE objectline_create.tpl.php -->
|
||||
|
||||
<tr class="liste_titre 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>
|
||||
@@ -178,7 +178,7 @@ else {
|
||||
|
||||
if (empty($senderissupplier))
|
||||
{
|
||||
$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, GETPOST('combinations', 'array'));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -210,7 +210,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';
|
||||
@@ -672,8 +679,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("#price_ttc").show(); // May no exists
|
||||
jQuery("#tva_tx").show();
|
||||
@@ -691,8 +698,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("#price_ttc").hide(); // May no exists
|
||||
jQuery("#tva_tx").hide();
|
||||
|
||||
@@ -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');
|
||||
@@ -285,7 +289,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')==='') // Unit price can be 0 but not ''
|
||||
if ($prod_entry_mode =='free' && GETPOST('price_ht')==='' && GETPOST('price_ttc')==='') // 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++;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
* 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
|
||||
@@ -45,6 +45,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');
|
||||
@@ -668,7 +672,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');
|
||||
@@ -699,22 +704,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')==='') // Unit price can be 0 but not ''
|
||||
if ($prod_entry_mode =='free' && GETPOST('price_ht')==='' && GETPOST('price_ttc')==='') // 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++;
|
||||
@@ -725,7 +730,21 @@ if (empty($reshook))
|
||||
$error++;
|
||||
}
|
||||
|
||||
if (GETPOST('prod_entry_mode') != 'free') // 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') // With combolist mode idprodfournprice is > 0 or -1. With autocomplete, idprodfournprice is > 0 or ''
|
||||
{
|
||||
$idprod=0;
|
||||
$productsupplier=new ProductFournisseur($db);
|
||||
|
||||
@@ -515,4 +515,3 @@ CREATE TABLE llx_oauth_state (
|
||||
|
||||
ALTER TABLE llx_product_batch ADD UNIQUE INDEX uk_product_batch (fk_product_stock, batch);
|
||||
|
||||
|
||||
|
||||
@@ -39,3 +39,39 @@ DROP TABLE llx_ecm_documents;
|
||||
|
||||
ALTER TABLE llx_notify ADD COLUMN type_target varchar(16) NULL;
|
||||
|
||||
-- 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);
|
||||
@@ -1,5 +1,5 @@
|
||||
-- ===================================================================
|
||||
-- Copyright (C) 2012 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
-- ============================================================================
|
||||
-- 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
|
||||
@@ -14,6 +14,6 @@
|
||||
-- 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_holiday_events ADD UNIQUE INDEX uk_holiday_name (name, entity);
|
||||
ALTER TABLE llx_product_attribute ADD CONSTRAINT unique_ref UNIQUE (ref);
|
||||
@@ -1,5 +1,5 @@
|
||||
-- ===================================================================
|
||||
-- Copyright (C) 2012 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
-- ============================================================================
|
||||
-- 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
|
||||
@@ -14,13 +14,13 @@
|
||||
-- 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_holiday_events
|
||||
CREATE TABLE llx_product_attribute
|
||||
(
|
||||
rowid integer NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
entity integer DEFAULT 1 NOT NULL, -- multi company id
|
||||
name VARCHAR( 255 ) NOT NULL,
|
||||
value TEXT NOT NULL
|
||||
)
|
||||
ENGINE=innodb;
|
||||
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
|
||||
);
|
||||
@@ -759,3 +759,5 @@ SearchIntoContracts=Contracts
|
||||
SearchIntoCustomerShipments=Customer shipments
|
||||
SearchIntoExpenseReports=Expense reports
|
||||
SearchIntoLeaves=Leaves
|
||||
|
||||
BulkActions=Bulk actions
|
||||
@@ -137,6 +137,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
|
||||
@@ -254,3 +255,38 @@ SizeUnits=Size unit
|
||||
DeleteProductBuyPrice=Delete buying price
|
||||
ConfirmDeleteProductBuyPrice=Are you sure you want to delete this buying price?
|
||||
|
||||
|
||||
#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>
|
||||
@@ -1574,6 +1574,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">';
|
||||
|
||||
@@ -839,6 +839,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;
|
||||
}
|
||||
@@ -944,6 +955,25 @@ class Product extends CommonObject
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.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');
|
||||
@@ -230,6 +237,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
|
||||
@@ -242,6 +252,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)
|
||||
@@ -264,6 +278,12 @@ else
|
||||
if ($search_tobatch != '' && $search_tobatch >= 0) $sql.= " AND p.tobatch = ".$db->escape($search_tobatch);
|
||||
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)
|
||||
{
|
||||
@@ -284,6 +304,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
|
||||
@@ -415,6 +438,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">';
|
||||
|
||||
Reference in New Issue
Block a user