diff --git a/Core/Lib/CostPriceTools.php b/Core/Lib/CostPriceTools.php
index 16eea7e563..a1fd36bbec 100644
--- a/Core/Lib/CostPriceTools.php
+++ b/Core/Lib/CostPriceTools.php
@@ -138,7 +138,7 @@ protected static function updateAveragePrice(Variante $variant): void
Where::gt('neto', 0),
];
foreach (ProductoProveedor::all($where, ['actualizado' => 'DESC'], 0, 0) as $prod) {
- $prices[] = $prod->neto;
+ $prices[] = static::calcularCosteProveedor($prod);
}
$newCost = empty($prices) ? 0.0 : array_sum($prices) / count($prices);
@@ -158,7 +158,7 @@ protected static function updateLastPrice(Variante $variant): void
Where::gt('neto', 0),
];
foreach (ProductoProveedor::all($where, ['actualizado' => 'DESC'], 0, 1) as $prod) {
- $variant->coste = round($prod->neto, Producto::ROUND_DECIMALS);
+ $variant->coste = round(static::calcularCosteProveedor($prod), Producto::ROUND_DECIMALS);
$variant->save();
break;
}
@@ -176,9 +176,19 @@ protected static function updateHighPrice(Variante $variant): void
Where::gt('neto', 0),
];
foreach (ProductoProveedor::all($where, ['precio' => 'DESC'], 0, 1) as $prod) {
- $variant->coste = round($prod->neto, Producto::ROUND_DECIMALS);
+ $variant->coste = round(static::calcularCosteProveedor($prod), Producto::ROUND_DECIMALS);
$variant->save();
break;
}
}
+
+ protected static function calcularCosteProveedor(ProductoProveedor $productoProveedor): float
+ {
+ return match (Tools::settings('default', 'calculocosteproveedor', 'neto')) {
+ 'precio' => $productoProveedor->precio,
+ 'netomasextra' => $productoProveedor->neto + $productoProveedor->precioextra,
+ 'preciomasextra' => $productoProveedor->precio + $productoProveedor->precioextra,
+ default => $productoProveedor->neto,
+ };
+ }
}
diff --git a/Core/Model/ProductoProveedor.php b/Core/Model/ProductoProveedor.php
index 5049593310..dcec1e3a2e 100644
--- a/Core/Model/ProductoProveedor.php
+++ b/Core/Model/ProductoProveedor.php
@@ -68,6 +68,9 @@ class ProductoProveedor extends ModelClass
/** @var float */
public $precio;
+ /** @var float */
+ public $precioextra;
+
/** @var string */
public $referencia;
@@ -113,6 +116,7 @@ public function clear(): void
$this->neto = 0.0;
$this->netoeuros = 0.0;
$this->precio = 0.0;
+ $this->precioextra = 0.0;
$this->stock = 0.0;
}
@@ -217,7 +221,7 @@ protected function onInsert(): void
*/
protected function onUpdate(): void
{
- if ($this->isDirty('neto')) {
+ if ($this->isDirty('neto') || $this->isDirty('precioextra')) {
CostPriceTools::update($this->getVariant());
}
diff --git a/Core/Table/productosprov.xml b/Core/Table/productosprov.xml
index c329209030..3e70251286 100644
--- a/Core/Table/productosprov.xml
+++ b/Core/Table/productosprov.xml
@@ -49,6 +49,11 @@
precio
double precision
+
+ precioextra
+ double precision
+ 0
+
referencia
character varying(30)
@@ -83,4 +88,4 @@
uniq_productosprov
UNIQUE (codproveedor,refproveedor,referencia,coddivisa)
-
\ No newline at end of file
+
diff --git a/Core/XMLView/EditProductoProveedor.xml b/Core/XMLView/EditProductoProveedor.xml
index efe18cb797..fd52169fa7 100644
--- a/Core/XMLView/EditProductoProveedor.xml
+++ b/Core/XMLView/EditProductoProveedor.xml
@@ -38,26 +38,29 @@
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/Core/XMLView/ListProductoProveedor.xml b/Core/XMLView/ListProductoProveedor.xml
index 896720bf38..0829a5ea0f 100644
--- a/Core/XMLView/ListProductoProveedor.xml
+++ b/Core/XMLView/ListProductoProveedor.xml
@@ -40,20 +40,23 @@
-
+
+
+
+
-
+
-
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/Core/XMLView/SettingsDefault.xml b/Core/XMLView/SettingsDefault.xml
index 653916acf9..05b90d8d04 100644
--- a/Core/XMLView/SettingsDefault.xml
+++ b/Core/XMLView/SettingsDefault.xml
@@ -119,10 +119,18 @@
high-price
-
+
+
+ neto
+ precio
+ netomasextra
+ preciomasextra
+
+
+
-
+
@@ -188,4 +196,4 @@
-
\ No newline at end of file
+
diff --git a/Test/Core/Lib/CostPriceToolsTest.php b/Test/Core/Lib/CostPriceToolsTest.php
index 9b89aff563..07e6819dbe 100644
--- a/Test/Core/Lib/CostPriceToolsTest.php
+++ b/Test/Core/Lib/CostPriceToolsTest.php
@@ -124,6 +124,62 @@ public function testLastPriceLeavesCosteUnchangedWhenAllZero(): void
$this->assertEquals(7.5, (float)$variant->coste, 'sin filas con neto > 0 el coste anterior debe preservarse');
}
+ public function testCalculoCosteProveedor(): void
+ {
+ $producto = $this->createProduct();
+ $variante = $producto->getVariants()[0];
+
+ $this->createSupplierProduct($producto, 100.0, '2024-01-01 00:00:00', 10.0, 5.0);
+ Tools::settingsSet('default', 'costpricepolicy', 'last-price');
+
+ $casos = [
+ 'neto' => 90.0,
+ 'precio' => 100.0,
+ 'netomasextra' => 95.0,
+ 'preciomasextra' => 105.0,
+ ];
+
+ foreach ($casos as $calculo => $coste) {
+ Tools::settingsSet('default', 'calculocosteproveedor', $calculo);
+ CostPriceTools::update($variante);
+
+ $variante->load($variante->idvariante);
+ $this->assertEquals($coste, (float)$variante->coste, 'bad-calculo-' . $calculo);
+ }
+ }
+
+ public function testPrecioMedioConPrecioExtra(): void
+ {
+ $producto = $this->createProduct();
+ $variante = $producto->getVariants()[0];
+
+ $this->createSupplierProduct($producto, 100.0, '2024-01-01 00:00:00', 10.0, 5.0);
+ $this->createSupplierProduct($producto, 200.0, '2024-02-01 00:00:00', 20.0, 10.0);
+
+ Tools::settingsSet('default', 'costpricepolicy', 'average-price');
+ Tools::settingsSet('default', 'calculocosteproveedor', 'netomasextra');
+ CostPriceTools::update($variante);
+
+ $variante->load($variante->idvariante);
+ $this->assertEquals(132.5, (float)$variante->coste, 'bad-average-price-extra');
+ }
+
+ public function testUltimoPrecioConPrecioExtra(): void
+ {
+ $producto = $this->createProduct();
+ $variante = $producto->getVariants()[0];
+
+ $this->createSupplierProduct($producto, 100.0, '2024-01-01 00:00:00', 0.0, 5.0);
+ $this->createSupplierProduct($producto, 200.0, '2024-02-01 00:00:00', 0.0, 20.0);
+
+ Tools::settingsSet('default', 'costpricepolicy', 'last-price');
+ Tools::settingsSet('default', 'calculocosteproveedor', 'preciomasextra');
+ CostPriceTools::update($variante);
+
+ $variante->load($variante->idvariante);
+ $this->assertEquals(220.0, (float)$variante->coste, 'bad-last-price-extra');
+ }
+
private function createProduct(): Producto
{
$product = $this->getRandomProduct();
@@ -137,7 +193,8 @@ private function createSupplierProduct(
Producto $product,
float $precio,
string $actualizado,
- float $dtopor = 0.0
+ float $dtopor = 0.0,
+ float $precioextra = 0.0
): ProductoProveedor {
// un proveedor distinto por fila para evitar el unique (codproveedor, refproveedor, referencia, coddivisa)
$supplier = $this->getRandomSupplier();
@@ -149,6 +206,7 @@ private function createSupplierProduct(
$row->idproducto = $product->idproducto;
$row->codproveedor = $supplier->codproveedor;
$row->precio = $precio;
+ $row->precioextra = $precioextra;
$row->dtopor = $dtopor;
$row->actualizado = $actualizado;
$this->assertTrue($row->save());
diff --git a/Test/Core/Model/ProductoProveedorTest.php b/Test/Core/Model/ProductoProveedorTest.php
index 80e6be040f..6ec7ea3aac 100644
--- a/Test/Core/Model/ProductoProveedorTest.php
+++ b/Test/Core/Model/ProductoProveedorTest.php
@@ -30,6 +30,7 @@
use FacturaScripts\Core\Model\Proveedor;
use FacturaScripts\Core\Model\Variante;
use FacturaScripts\Core\Tools;
+use FacturaScripts\Core\Where;
use FacturaScripts\Core\WorkQueue;
use FacturaScripts\Test\Traits\DefaultSettingsTrait;
use FacturaScripts\Test\Traits\LogErrorsTrait;
@@ -339,9 +340,116 @@ public function testClear(): void
$this->assertEquals(0, $productoProveedor->neto);
$this->assertEquals(0, $productoProveedor->netoeuros);
$this->assertEquals(0, $productoProveedor->precio);
+ $this->assertEquals(0, $productoProveedor->precioextra);
$this->assertEquals(0, $productoProveedor->stock);
}
+ public function testPrecioExtra(): void
+ {
+ $proveedor = $this->getRandomSupplier();
+ $this->assertTrue($proveedor->save());
+
+ $producto = $this->getRandomProduct();
+ $this->assertTrue($producto->save());
+
+ $productoProveedor = new ProductoProveedor();
+ $productoProveedor->codproveedor = $proveedor->codproveedor;
+ $productoProveedor->idproducto = $producto->idproducto;
+ $productoProveedor->precio = 100;
+ $productoProveedor->precioextra = 12.34;
+ $productoProveedor->referencia = $producto->referencia;
+ $this->assertTrue($productoProveedor->save());
+
+ $recargado = new ProductoProveedor();
+ $this->assertTrue($recargado->load($productoProveedor->id));
+ $this->assertEquals(12.34, (float)$recargado->precioextra);
+
+ $this->assertTrue($productoProveedor->delete());
+ $this->assertTrue($producto->delete());
+ $this->assertTrue($proveedor->getDefaultAddress()->delete());
+ $this->assertTrue($proveedor->delete());
+ }
+
+ public function testPrecioExtraActualizaCoste(): void
+ {
+ Tools::settingsSet('default', 'costpricepolicy', 'last-price');
+ Tools::settingsSet('default', 'calculocosteproveedor', 'netomasextra');
+
+ $proveedor = $this->getRandomSupplier();
+ $this->assertTrue($proveedor->save());
+
+ $producto = $this->getRandomProduct();
+ $this->assertTrue($producto->save());
+ $variante = $producto->getVariants()[0];
+
+ $productoProveedor = new ProductoProveedor();
+ $productoProveedor->codproveedor = $proveedor->codproveedor;
+ $productoProveedor->idproducto = $producto->idproducto;
+ $productoProveedor->precio = 100;
+ $productoProveedor->precioextra = 5;
+ $productoProveedor->referencia = $producto->referencia;
+ $this->assertTrue($productoProveedor->save());
+
+ $variante->reload();
+ $this->assertEquals(105.0, (float)$variante->coste);
+
+ $productoProveedor->precioextra = 15;
+ $this->assertTrue($productoProveedor->save());
+
+ $variante->reload();
+ $this->assertEquals(115.0, (float)$variante->coste);
+
+ $this->assertTrue($productoProveedor->delete());
+ $this->assertTrue($producto->delete());
+ $this->assertTrue($proveedor->getDefaultAddress()->delete());
+ $this->assertTrue($proveedor->delete());
+ }
+
+ public function testActualizarDesdeCompraMantienePrecioExtra(): void
+ {
+ Tools::settingsSet('default', 'updatesupplierprices', true);
+
+ [$proveedor, $producto, $albaran] = $this->getAlbaranConLineaProducto();
+
+ $productoProveedor = ProductoProveedor::all([
+ Where::eq('referencia', $producto->referencia),
+ Where::eq('codproveedor', $proveedor->codproveedor),
+ ])[0];
+ $productoProveedor->actualizado = Tools::dateTime('- 1 day');
+ $productoProveedor->precioextra = 12.5;
+ $this->assertTrue($productoProveedor->save());
+
+ $albaran2 = new AlbaranProveedor();
+ $albaran2->setSubject($proveedor);
+ $this->assertTrue($albaran2->save());
+
+ $linea = $albaran2->getNewProductLine($producto->referencia);
+ $linea->pvpunitario = 25;
+ $linea->dtopor = 10;
+ $linea->dtopor2 = 0;
+ $this->assertTrue($linea->save());
+ $lineas = [$linea];
+ $this->assertTrue(Calculator::calculate($albaran2, $lineas, true));
+
+ while (true) {
+ if (false === WorkQueue::run()) {
+ break;
+ }
+ }
+
+ $productoProveedor->load($productoProveedor->id);
+ $this->assertEquals(12.5, (float)$productoProveedor->precioextra);
+ $this->assertEquals(25.0, (float)$productoProveedor->precio);
+ $this->assertEquals(22.5, (float)$productoProveedor->neto);
+
+ $this->assertTrue($albaran->delete());
+ $this->assertTrue($albaran2->delete());
+ $this->assertTrue($proveedor->getDefaultAddress()->delete());
+ $this->assertTrue($proveedor->delete());
+ $this->assertTrue($productoProveedor->delete());
+ $this->assertTrue($producto->delete());
+ }
+
public function testGetVariant(): void
{
$producto = $this->getRandomProduct();