diff --git a/htdocs/product/class/api_products.class.php b/htdocs/product/class/api_products.class.php index cdc3d267433..846ca3568d0 100644 --- a/htdocs/product/class/api_products.class.php +++ b/htdocs/product/class/api_products.class.php @@ -1173,6 +1173,49 @@ class Products extends DolibarrApi return $this->_cleanObjectDatas($product_fourn_list); } + /** + * Get the history logs for all supplier prices of a specific product + * + * @since 26.0.0 Initial implementation + * + * @param int $id ID of product + * @param string $ref Ref of element + * @param string $ref_ext Ref ext of element + * @param string $barcode Barcode of element + * @return array Array of price logs + * @phan-return array + * @phpstan-return array + * + * @url GET {id}/purchase_prices/logs + * + * @throws RestException 400 + * @throws RestException 403 + * @throws RestException 404 + */ + public function getPurchasePriceLogs($id, $ref = '', $ref_ext = '', $barcode = '') + { + dol_syslog(__METHOD__, LOG_DEBUG); + + if (empty($id) && empty($ref) && empty($ref_ext) && empty($barcode)) { + throw new RestException(400, 'bad value for parameter id, ref, ref_ext or barcode'); + } + $id = (empty($id) ? 0 : $id); + if (!DolibarrApiAccess::$user->hasRight('produit', 'lire')) { + throw new RestException(403); + } + $result = $this->product->fetch($id, $ref, $ref_ext, $barcode); + if (!$result) { + throw new RestException(404, 'Product not found'); + } + if (!DolibarrApi::_checkAccessToResource('product', $this->product->id)) { + throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login); + } + + $allLogs = $this->product->fetchAllPriceLogs($this->product->id); + + return $allLogs; + } + /** * Get attributes * diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 72133371fa8..b2f290082a7 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2362,6 +2362,55 @@ class Product extends CommonObject } } + /** + * Get all price change logs for a product, enriched with supplier info + * + * @param int $id Id of the product + * @return array Array of log objects with supplier info + * @phan-return array + * @phpstan-return array + */ + public function fetchAllPriceLogs($id) + { + dol_syslog(__METHOD__, LOG_DEBUG); + + $logs = array(); + + $sql = "SELECT"; + $sql .= " pl.rowid as log_rowid,"; + $sql .= " pl.datec,"; + $sql .= " pl.price,"; + $sql .= " pl.quantity,"; + $sql .= " pl.fk_user,"; + $sql .= " pl.multicurrency_code,"; + $sql .= " pl.multicurrency_price,"; + // Supplier info + $sql .= " pp.fk_soc as supplier_id,"; + $sql .= " pp.ref_fourn as supplier_ref,"; + $sql .= " pp.entity as entity"; + + $sql .= " FROM " . $this->db->prefix() . "product_fournisseur_price_log as pl"; + $sql .= " INNER JOIN " . $this->db->prefix() . "product_fournisseur_price as pp ON pl.fk_product_fournisseur = pp.rowid"; + + $sql .= " WHERE pp.fk_product = " . ((int) $id); + $sql .= " AND pp.entity IN (" . getEntity('product') . ")"; + $sql .= " ORDER BY pl.datec DESC"; + + $resql = $this->db->query($sql); + if ($resql) { + while ($obj = $this->db->fetch_object($resql)) { + if (is_string($obj->datec)) { + $obj->datec = strtotime($obj->datec); + } + $logs[] = $obj; + } + $this->db->free($resql); + } else { + $this->error = $this->db->lasterror(); + } + + return $logs; + } /** * Return price of sell of a product for a seller/buyer/product. diff --git a/test/hurl/api/products/10_product_price_logs.hurl b/test/hurl/api/products/10_product_price_logs.hurl new file mode 100644 index 00000000000..5d47109ae0c --- /dev/null +++ b/test/hurl/api/products/10_product_price_logs.hurl @@ -0,0 +1,31 @@ +# ============================================================================== +# Dynamic Product Purchase Price Logs Tests +# ============================================================================== + +# SETUP: Capture a valid Product ID from the first available product +GET http://{{hostnport}}/api/index.php/products?limit=1 +HTTP 200 +[Captures] +target_id: jsonpath "$[0].id" + +# Validation: Test with ID 0 (Should fail) +GET http://{{hostnport}}/api/index.php/products/0/purchase_prices/logs +HTTP 400 +{"error":{"code":400,"message":"Bad Request: bad value for parameter id, ref, ref_ext or barcode"}} + +# Validation: Test with a non-existent ID (Should fail) +GET http://{{hostnport}}/api/index.php/products/999999/purchase_prices/logs +HTTP 404 +{"error":{"code":404,"message":"Not Found: Product not found"}} + +# Success: Fetch logs using the CAPTURED ID +GET http://{{hostnport}}/api/index.php/products/{{ target_id }}/purchase_prices/logs +HTTP 200 + +# Query Param: Test with pagination_data=false +GET http://{{hostnport}}/api/index.php/products/{{ target_id }}/purchase_prices/logs?pagination_data=false +HTTP 200 + +# Query Param: Test with pagination_data=true +GET http://{{hostnport}}/api/index.php/products/{{ target_id }}/purchase_prices/logs?pagination_data=true +HTTP 200