docs(architecture): document shipment review workflow and ADRs

Add comprehensive Architecture Decision Records (ADRs) detailing the
new shipment review state machine, review history domain, and customer
resubmission logic. This documentation establishes the separation
between operational shipment status and the review lifecycle.

Additionally, perform repository cleanup by removing obsolete Postman
collections, environment files, test scripts, and unused migrations.

- Add ADR-001 through ADR-005 regarding review workflow and state.
- Add shipment review handoff documentation.
- Remove redundant Postman resources and local environment configs.
- Remove `test_pdf_generation.php` and `resubmit-test.json`.
- Remove unused `shipment_packages` migration.
- Rename and reorganize one-off maintenance scripts.
This commit is contained in:
Kazem Alghasi 2026-10-03 06:20:32 +03:30
parent 0209b6b32e
commit 78d21942db
14 changed files with 0 additions and 276 deletions

View File

@ -1,9 +0,0 @@
# Use this workspace to collaborate
workspace:
id: c5409f98-698e-4a6c-8edb-443e130b005c
# All resources in the `postman/` folder are automatically registered in Local View.
# Point to additional files outside the `postman/` folder to register them individually. Example:
#localResources:
# collections:
# - ../tests/E2E Test Collection/

View File

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (!Schema::hasTable('shipment_packages')) {
Schema::create('shipment_packages', function (Blueprint $table) {
$table->id();
$table->foreignId('shipment_id')->constrained()->cascadeOnDelete();
$table->unsignedTinyInteger('package_number')->default(1); // ۱، ۲، ۳، ...
$table->decimal('weight', 10, 3)->default(0); // وزن واقعی (kg)
$table->decimal('volumetric_weight', 10, 3)->default(0); // وزن حجمی (kg)
$table->decimal('chargeable_weight', 10, 3)->default(0); // وزن قابل محاسبه
$table->string('dimensions', 50)->nullable(); // ابعاد W*L*H (cm)
$table->text('description')->nullable(); // توضیحات بسته
$table->timestamps();
$table->index('shipment_id');
});
}
}
public function down(): void
{
Schema::dropIfExists('shipment_packages');
}
};

View File

View File

@ -1,2 +0,0 @@
$kind: collection
name: New Collection

View File

@ -1,8 +0,0 @@
name: IFNEX Local
values:
- key: base_url
value: ''
- key: token
value: ''
- key: ''
value: ''

View File

@ -1,6 +0,0 @@
name: IFNEX Local2
values:
- key: base_url
value: ''
- key: token
value: ''

View File

@ -1,2 +0,0 @@
name: Globals
values: []

View File

@ -1,36 +0,0 @@
{
"direction": "export",
"type": "PARCEL",
"from_country_id": 96,
"to_country_id": 7,
"weight": 3.70,
"dimensions": "90*90*90",
"packages": [
{
"weight": 3.70,
"dimensions": "90*90*90",
"description": "dfasdf"
}
],
"sender_name": "Moshtari Updated",
"sender_phone": "989898989899",
"sender_address": "New addresssss",
"sender_city": "asdfasdf",
"receiver_name": "moshtari2",
"receiver_phone": "565656565",
"receiver_address": "sdfhsdfsdhsdgh",
"receiver_city": "dfgsdfgsdfgs",
"extra_service": 0,
"packing_cost": 0,
"domestic_pickup": 0,
"domestic_delivery": 0,
"warehousing_cost": 0,
"items": [
{
"description": "HHHH25",
"hs_code": "989898989",
"quantity": 12,
"unit_price": 1.20
}
]
}

View File

@ -1,145 +0,0 @@
<?php
/**
* اسکریپت تست تولید PDF برای IFNEX
*
* این اسکریپت یه محموله نمونه می‌سازه و سه تا PDF (AWB, Invoice, Label) تولید می‌کنه
* و اون‌ها رو توی پوشه storage/app/pdf-test/ ذخیره می‌کنه.
*
* استفاده:
* php test_pdf_generation.php
*
* پس از اجرا، فایل‌های تولیدشده رو بررسی کنید:
* storage/app/pdf-test/AWB-test.pdf
* storage/app/pdf-test/INVOICE-test.pdf
* storage/app/pdf-test/LABEL-test.pdf
*/
require __DIR__ . '/vendor/autoload.php';
$app = require_once __DIR__ . '/bootstrap/app.php';
$app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
use App\Models\Shipment;
use App\Models\ShipmentItem;
use App\Models\Country;
use App\Services\PdfService;
use Illuminate\Support\Facades\File;
echo "═══════════════════════════════════════════\n";
echo "🧪 IFNEX PDF Generation Test\n";
echo "═══════════════════════════════════════════\n\n";
// ─── ۱. بررسی نصب بودن کتابخانه بارکد ───
echo "1️⃣ Checking barcode library...\n";
if (class_exists(\Picqer\Barcode\BarcodeGeneratorPNG::class)) {
echo " ✅ picqer/php-barcode-generator is installed\n\n";
} else {
echo " ❌ picqer/php-barcode-generator is NOT installed\n";
echo " Run: composer require picqer/php-barcode-generator\n\n";
exit(1);
}
// ─── ۲. ایجاد پوشه تست ───
$testDir = storage_path('app/pdf-test');
if (!File::exists($testDir)) {
File::makeDirectory($testDir, 0755, true);
echo "2️⃣ Created test directory: {$testDir}\n\n";
} else {
echo "2️⃣ Test directory exists: {$testDir}\n\n";
}
// ─── ۳. پیدا کردن یه محموله نمونه ───
echo "3️⃣ Finding sample shipment...\n";
$shipment = Shipment::with(['fromCountry', 'toCountry', 'items'])->latest()->first();
if (!$shipment) {
echo " ❌ No shipment found in database. Please create one first.\n";
exit(1);
}
echo " ✅ Found shipment: {$shipment->awb_no}\n";
echo " - Type: {$shipment->type?->value}\n";
echo " - Direction: {$shipment->direction?->value}\n";
echo " - From: {$shipment->fromCountry?->name}\n";
echo " - To: {$shipment->toCountry?->name}\n";
echo " - Items: {$shipment->items->count()}\n\n";
// ─── ۴. اگر آیتم نداره، یه آیتم تستی اضافه کن ───
if ($shipment->items->isEmpty()) {
echo " ⚠️ Shipment has no items. Adding test item...\n";
ShipmentItem::create([
'shipment_id' => $shipment->id,
'row_number' => 1,
'description' => 'Electronics PCB Board',
'hs_code' => '8542390001',
'quantity' => 104,
'unit_price' => 1.10,
'total_usd' => 114.40,
]);
$shipment->load('items');
echo " ✅ Added test item\n\n";
}
// ─── ۵. تولید AWB PDF ───
echo "4️⃣ Generating AWB PDF...\n";
try {
$pdfService = app(PdfService::class);
$awbContent = $pdfService->awb($shipment);
$awbPath = $testDir . '/AWB-' . $shipment->awb_no . '.pdf';
File::put($awbPath, $awbContent);
echo " ✅ AWB PDF saved: {$awbPath}\n";
echo " Size: " . number_format(strlen($awbContent) / 1024, 2) . " KB\n\n";
} catch (\Throwable $e) {
echo " ❌ AWB PDF failed: " . $e->getMessage() . "\n\n";
}
// ─── ۶. تولید Invoice PDF ───
echo "5️⃣ Generating Invoice PDF...\n";
try {
$invoiceContent = $pdfService->invoice($shipment);
$invoicePath = $testDir . '/INVOICE-' . $shipment->awb_no . '.pdf';
File::put($invoicePath, $invoiceContent);
echo " ✅ Invoice PDF saved: {$invoicePath}\n";
echo " Size: " . number_format(strlen($invoiceContent) / 1024, 2) . " KB\n\n";
} catch (\InvalidArgumentException $e) {
echo " ⚠️ Invoice skipped: " . $e->getMessage() . "\n";
echo " (این طبیعی است اگر محموله DOC است)\n\n";
} catch (\Throwable $e) {
echo " ❌ Invoice PDF failed: " . $e->getMessage() . "\n\n";
}
// ─── ۷. تولید Label PDF ───
echo "6️⃣ Generating Label PDF...\n";
try {
$labelContent = $pdfService->label($shipment);
$labelPath = $testDir . '/LABEL-' . $shipment->awb_no . '.pdf';
File::put($labelPath, $labelContent);
echo " ✅ Label PDF saved: {$labelPath}\n";
echo " Size: " . number_format(strlen($labelContent) / 1024, 2) . " KB\n\n";
} catch (\Throwable $e) {
echo " ❌ Label PDF failed: " . $e->getMessage() . "\n\n";
}
// ─── ۸. تست محموله DOC (بدون فاکتور) ───
echo "7️⃣ Testing DOC shipment (should skip invoice)...\n";
$docShipment = Shipment::where('type', 'DOC_NORMAL')->first();
if ($docShipment) {
try {
$docShipment->load('items');
if ($docShipment->items->isEmpty()) {
$pdfService->invoice($docShipment);
echo " ❌ ERROR: Should have thrown exception for DOC shipment!\n\n";
} else {
echo " ℹ️ DOC shipment has items, invoice would work\n\n";
}
} catch (\InvalidArgumentException $e) {
echo " ✅ Correctly skipped invoice for DOC: " . $e->getMessage() . "\n\n";
}
} else {
echo " ℹ️ No DOC shipment found for testing (skipped)\n\n";
}
echo "═══════════════════════════════════════════\n";
echo "✅ Test completed!\n";
echo "═══════════════════════════════════════════\n\n";
echo "📁 Check the generated PDFs at:\n";
echo " {$testDir}\n\n";

View File

@ -1,36 +0,0 @@
{
"direction": "export",
"type": "PARCEL",
"from_country_id": 96,
"to_country_id": 7,
"weight": 3.70,
"dimensions": "90*90*90",
"packages": [
{
"weight": 3.70,
"dimensions": "90*90*90",
"description": "dfasdf"
}
],
"sender_name": "Moshtari Updated",
"sender_phone": "989898989899",
"sender_address": "New addresssss",
"sender_city": "asdfasdf",
"receiver_name": "moshtari2",
"receiver_phone": "565656565",
"receiver_address": "sdfhsdfsdhsdgh",
"receiver_city": "dfgsdfgsdfgs",
"extra_service": 0,
"packing_cost": 0,
"domestic_pickup": 0,
"domestic_delivery": 0,
"warehousing_cost": 0,
"items": [
{
"description": "HHHH25",
"hs_code": "989898989",
"quantity": 12,
"unit_price": 1.20
}
]
}