3
0
mirror of https://github.com/snipe/snipe-it.git synced 2026-08-18 11:15:42 +00:00

And more tests

This commit is contained in:
snipe
2026-08-05 11:30:04 +01:00
parent 0544d1519a
commit c0eccdd4a4
7 changed files with 223 additions and 11 deletions

View File

@ -50,7 +50,7 @@ class AdjustAccessoryQuantityApiTest extends TestCase
$this->assertSame('restock from PO', $log->note);
// order_number moved off the log row to the Orders table — the
// log carries order_id pointing at the newly-created Order.
$order = \App\Models\Order::findOrFail($log->order_id);
$order = $log->orderItem->order;
$this->assertSame('PO-API-1', $order->order_number);
$this->assertSame($actor->id, (int) $log->created_by);
}
@ -163,4 +163,118 @@ class AdjustAccessoryQuantityApiTest extends TestCase
->count(), 'No separate uploaded log entry should have been created.');
$this->assertSame($uploadsBefore + 1, $accessory->uploads()->count());
}
public function test_supplier_purchase_date_unit_cost_and_currency_land_on_order_and_order_item()
{
$accessory = Accessory::factory()->create(['qty' => 2]);
$supplier = \App\Models\Supplier::factory()->create();
$this->actingAsForApi(User::factory()->editAccessories()->create())
->postJson(route('api.accessories.adjust-quantity', $accessory), [
'amount' => 5,
'note' => 'PO arrived',
'order_number' => 'PO-FULL-META',
'supplier_id' => $supplier->id,
'purchase_date' => '2026-03-15',
'unit_cost' => 12.3456,
'currency' => 'EUR',
])
->assertOk();
$log = Actionlog::where('item_type', Accessory::class)
->where('item_id', $accessory->id)
->where('action_type', ActionType::QuantityAdjust->value)
->latest('id')
->firstOrFail();
$order = $log->orderItem->order;
$this->assertSame('PO-FULL-META', $order->order_number);
$this->assertSame($supplier->id, (int) $order->supplier_id);
$this->assertSame('2026-03-15', $order->purchase_date->toDateString());
$this->assertSame('EUR', $order->currency);
$line = $order->orderItems()->firstOrFail();
$this->assertSame(Accessory::class, $line->item_type);
$this->assertSame($accessory->id, (int) $line->item_id);
$this->assertSame(5, (int) $line->qty);
$this->assertEquals(12.3456, (float) $line->price);
}
public function test_audit_only_zero_delta_with_no_order_metadata_skips_order_creation()
{
// Zero delta and no supplier / order number / date / cost /
// currency = pure inventory audit. No Order row should be
// created for these, since there was no transaction.
$accessory = Accessory::factory()->create(['qty' => 5]);
$ordersBefore = \App\Models\Order::count();
$this->actingAsForApi(User::factory()->editAccessories()->create())
->postJson(route('api.accessories.adjust-quantity', $accessory), [
'amount' => 0,
'note' => 'shelf count matches',
])
->assertOk();
$this->assertSame($ordersBefore, \App\Models\Order::count());
$log = Actionlog::where('item_type', Accessory::class)
->where('item_id', $accessory->id)
->where('action_type', ActionType::QuantityAdjust->value)
->latest('id')
->firstOrFail();
$this->assertNull($log->order_item_id);
}
public function test_multiple_adjusts_with_same_order_number_dedupe_to_one_order()
{
// Repeated qty adjusts referencing the same order_number should
// reuse the existing Order row (dedupe on order_number +
// supplier + company), while each adjust still produces its own
// OrderItem line (one line per event).
$accessory = Accessory::factory()->create(['qty' => 0]);
$supplier = \App\Models\Supplier::factory()->create();
$actor = User::factory()->editAccessories()->create();
foreach ([3, 4] as $delta) {
$this->actingAsForApi($actor)
->postJson(route('api.accessories.adjust-quantity', $accessory), [
'amount' => $delta,
'note' => 'staggered receipt',
'order_number' => 'PO-DEDUPE',
'supplier_id' => $supplier->id,
])
->assertOk();
}
$orders = \App\Models\Order::where('order_number', 'PO-DEDUPE')->get();
$this->assertCount(1, $orders, 'Same order_number should reuse the existing Order row.');
$items = $orders->first()->orderItems;
$this->assertCount(2, $items, 'Each adjust event should have its own OrderItem line.');
$this->assertEqualsCanonicalizing([3, 4], $items->pluck('qty')->map(fn ($q) => (int) $q)->all());
}
public function test_blank_order_number_creates_a_distinct_order_per_event()
{
// A blank order_number is a distinct transaction each time, not
// a bucket to pool anonymous receipts into. Each adjust with
// no order_number gets its own Order row.
$accessory = Accessory::factory()->create(['qty' => 0]);
$supplier = \App\Models\Supplier::factory()->create();
$actor = User::factory()->editAccessories()->create();
$ordersBefore = \App\Models\Order::count();
foreach ([2, 3] as $delta) {
$this->actingAsForApi($actor)
->postJson(route('api.accessories.adjust-quantity', $accessory), [
'amount' => $delta,
'note' => 'anonymous receipt',
'supplier_id' => $supplier->id,
])
->assertOk();
}
$this->assertSame($ordersBefore + 2, \App\Models\Order::count());
}
}

View File

@ -185,7 +185,7 @@ class UpdateAccessoryTest extends TestCase implements TestsFullMultipleCompanies
->firstOrFail();
$this->assertSame(7, (int) $log->quantity);
$this->assertSame('PO-API', \App\Models\Order::findOrFail($log->order_id)->order_number);
$this->assertSame('PO-API', $log->orderItem->order->order_number);
$this->assertNotEmpty($log->note, 'Adjustment note must be synthesized when the API caller omits one');
}

View File

@ -51,7 +51,7 @@ class AdjustAccessoryQuantityTest extends TestCase
$this->assertNotNull($log);
$this->assertSame(3, (int) $log->quantity);
$this->assertSame('restock from PO', $log->note);
$this->assertSame('PO-42', \App\Models\Order::findOrFail($log->order_id)->order_number);
$this->assertSame('PO-42', $log->orderItem->order->order_number);
$this->assertSame($actor->id, (int) $log->created_by);
}
@ -76,7 +76,7 @@ class AdjustAccessoryQuantityTest extends TestCase
->first();
$this->assertSame(-4, (int) $log->quantity);
$this->assertNull($log->order_id);
$this->assertNull($log->order_item_id);
}
public function test_decrement_below_zero_is_rejected()

View File

@ -74,10 +74,7 @@ class StoreAssetTest extends TestCase
$this->assertTrue($asset->model->is($model));
$this->assertEquals('A New Asset', $asset->name);
$this->assertEquals('Some notes', $asset->notes);
// order_number moved off the parent Asset column to the Orders /
// OrderItems data model. A create-time order_number in the request
// body no longer lands on the model — acquisition tracking is via
// the adjust-quantity flow or the CSV importer.
$this->assertEquals('5678', $asset->order_number);
$this->assertEquals('123.45', $asset->purchase_cost);
$this->assertTrue($asset->purchase_date->is('2023-09-02'));
$this->assertEquals('1', $asset->requestable);

View File

@ -95,8 +95,7 @@ class UpdateAssetTest extends TestCase
$this->assertTrue($updatedAsset->model->is($model));
$this->assertEquals('A New Asset', $updatedAsset->name);
$this->assertEquals('Some notes', $updatedAsset->notes);
// See StoreAssetTest for why order_number is no longer on the
// parent Asset column.
$this->assertEquals('5678', $updatedAsset->order_number);
$this->assertEquals('123.45', $updatedAsset->purchase_cost);
$this->assertTrue($updatedAsset->purchase_date->is('2023-09-02'));
$this->assertEquals('1', $updatedAsset->requestable);

View File

@ -0,0 +1,102 @@
<?php
namespace Tests\Feature\Assets;
use App\Models\Asset;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Supplier;
use App\Models\User;
use Tests\TestCase;
/**
* AssetObserver::created writes a matching Order + OrderItem for every
* new asset regardless of how the asset arrived (web form, API, CSV
* importer). Dedupe semantics match the adjust-quantity flow: a
* populated order_number shares the Order row with same-tuple assets,
* a blank one gets its own Order per event.
*/
class AssetObserverOrderTest extends TestCase
{
public function test_asset_create_with_order_number_writes_matching_order_and_order_item(): void
{
$actor = User::factory()->createAssets()->create();
$supplier = Supplier::factory()->create();
$asset = Asset::factory()->create([
'created_by' => $actor->id,
'company_id' => $actor->company_id,
'supplier_id' => $supplier->id,
'order_number' => 'PO-OBS-1',
'purchase_date' => '2026-04-01',
'purchase_cost' => 199.99,
]);
$line = OrderItem::where('item_type', Asset::class)
->where('item_id', $asset->id)
->firstOrFail();
$this->assertSame(1, (int) $line->qty);
$this->assertEquals(199.99, (float) $line->price);
$order = Order::findOrFail($line->order_id);
$this->assertSame('PO-OBS-1', $order->order_number);
$this->assertSame($supplier->id, (int) $order->supplier_id);
$this->assertSame($actor->company_id, $order->company_id);
$this->assertSame('2026-04-01', $order->purchase_date->toDateString());
}
public function test_two_assets_with_same_order_tuple_share_one_order_row(): void
{
$actor = User::factory()->createAssets()->create();
$supplier = Supplier::factory()->create();
$shared = [
'created_by' => $actor->id,
'company_id' => $actor->company_id,
'supplier_id' => $supplier->id,
'order_number' => 'PO-OBS-SHARED',
'purchase_date' => '2026-04-02',
];
$first = Asset::factory()->create($shared + ['purchase_cost' => 100]);
$second = Asset::factory()->create($shared + ['purchase_cost' => 200]);
$orders = Order::where('order_number', 'PO-OBS-SHARED')
->where('supplier_id', $supplier->id)
->where('company_id', $actor->company_id)
->get();
$this->assertCount(1, $orders, 'Same (order_number, supplier, company) tuple must dedupe.');
$lineIds = OrderItem::where('item_type', Asset::class)
->whereIn('item_id', [$first->id, $second->id])
->pluck('order_id')
->unique()
->values();
$this->assertCount(1, $lineIds);
$this->assertSame($orders->first()->id, (int) $lineIds->first());
}
public function test_blank_order_number_creates_a_distinct_order_per_asset(): void
{
$actor = User::factory()->createAssets()->create();
$supplier = Supplier::factory()->create();
$ordersBefore = Order::count();
$shared = [
'created_by' => $actor->id,
'company_id' => $actor->company_id,
'supplier_id' => $supplier->id,
'order_number' => null,
];
Asset::factory()->create($shared);
Asset::factory()->create($shared);
// Blank order_number = distinct transaction, so both assets get
// their own Order rows even though supplier + company match.
$this->assertSame($ordersBefore + 2, Order::count());
}
}

View File

@ -46,7 +46,7 @@ class AdjustComponentQuantityApiTest extends TestCase
->firstOrFail();
$this->assertSame(3, (int) $log->quantity);
$this->assertSame('PO-CMP', \App\Models\Order::findOrFail($log->order_id)->order_number);
$this->assertSame('PO-CMP', $log->orderItem->order->order_number);
}
public function test_note_is_required()