From 1e2effdc061b30e06d8bb3a438e43e12129bdd1e Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 27 May 2026 00:34:58 +0200 Subject: [PATCH] Tarea #4687 - agregar campo precio extra --- Core/Lib/CostPriceTools.php | 16 +++- Core/Model/ProductoProveedor.php | 6 +- Core/Table/productosprov.xml | 7 +- Core/XMLView/EditProductoProveedor.xml | 17 ++-- Core/XMLView/ListProductoProveedor.xml | 15 +-- Core/XMLView/SettingsDefault.xml | 14 ++- Test/Core/Lib/CostPriceToolsTest.php | 60 +++++++++++- Test/Core/Model/ProductoProveedorTest.php | 108 ++++++++++++++++++++++ 8 files changed, 221 insertions(+), 22 deletions(-) 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();