Add formula-based product pricing (P/W/MG) for Parsian-style unit prices.

Port FormulaPriceService from colleague app so admins can compute a single unit price from reference mid, weight, and wage instead of offset coefficients.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-10 16:47:29 +03:30
co-authored by Cursor
parent 71a48c9c08
commit f421fcb83f
9 changed files with 515 additions and 9 deletions
@@ -15,11 +15,13 @@ use Illuminate\Support\Facades\Http;
use \App\Http\Controllers\Customer\FundController;
use App\Http\Controllers\Site\ReportController;
use App\Services\ConfigService;
use App\Services\FormulaPriceService;
use App\Services\ProductDisplayPriceService;
use App\Services\ProductPriceService;
use App\Services\ProductPriceSyncService;
use App\Services\PriceBroadcastService;
use App\Services\CategoryService;
use App\Services\ZaarboxPriceService;
use Illuminate\Support\Facades\Schema;
@@ -117,6 +119,11 @@ class ProductController extends SuperController
$data = $this->filterProductData($request->all());
$product = Product::create($data);
if (FormulaPriceService::isEnabled($product) && $product->reading_method) {
app(ZaarboxPriceService::class)->syncProductFromApi($product);
}
$this->broadcastPricesAfterResponse(true);
return $this->resultCreate($product->id);
@@ -134,7 +141,10 @@ class ProductController extends SuperController
}
// قیمت دستی ⇒ تعدیل مرجع بی‌معناست و صفر می‌شود.
// قیمت اپ در filterProductData عمداً price=0 می‌گذارد؛ نباید تعدیل را پاک کند.
if (isset($data['price']) && empty($data['app_price'])) {
// قیمت فرمولی reading_method می‌خواهد — با وجود price ذخیره‌شده مرجع را پاک نکن.
$formulaEnabled = Product::hasPriceFormulaColumns()
&& filter_var($data['price_formula_enabled'] ?? false, FILTER_VALIDATE_BOOLEAN);
if (isset($data['price']) && empty($data['app_price']) && ! $formulaEnabled) {
$data['reading_method'] = null;
if (Product::hasPriceOffsetColumn()) {
$data['price_offset_percent'] = 0;
@@ -166,6 +176,7 @@ class ProductController extends SuperController
&& $product->reading_method
&& array_key_exists('price_offset_percent', $data)
&& !$clearingReadingMethod
&& ! $formulaEnabled
) {
ProductPriceService::reapplyOffsetPercent(
$product,
@@ -176,6 +187,12 @@ class ProductController extends SuperController
}
$product->update($data);
$product->refresh();
if (FormulaPriceService::isEnabled($product) && $product->reading_method) {
app(ZaarboxPriceService::class)->syncProductFromApi($product);
}
$this->broadcastPricesAfterResponse(true);
return response()->json(true);
@@ -659,6 +676,35 @@ class ProductController extends SuperController
}
$data['price'] = 0;
$data['price_profit'] = 0;
if (Product::hasPriceFormulaColumns()) {
$data['price_formula_enabled'] = false;
}
}
}
if (!Product::hasPriceFormulaColumns()) {
unset($data['price_formula_enabled'], $data['price_formula']);
} else {
$formulaEnabled = filter_var($data['price_formula_enabled'] ?? false, FILTER_VALIDATE_BOOLEAN);
$data['price_formula_enabled'] = $formulaEnabled;
$formula = trim((string) ($data['price_formula'] ?? ''));
if ($formulaEnabled) {
if ($formula === '') {
$formula = FormulaPriceService::DEFAULT_FORMULA;
}
$data['price_formula'] = $formula;
$data['app_price'] = false;
if (Product::hasDirectPriceColumn()) {
$data['direct_price'] = 1;
}
if (Product::hasPriceOffsetColumn()) {
$data['price_offset_percent'] = 0;
}
if (empty($data['reading_method'])) {
throw new UnprocessableEntityHttpException('برای قیمت فرمولی، یک مرجع قیمت انتخاب کنید');
}
} else {
$data['price_formula'] = $formula !== '' ? $formula : null;
}
}