mirror of
https://github.com/snipe/snipe-it.git
synced 2026-08-18 11:15:42 +00:00
Use ITEM_TYPES constant
This commit is contained in:
@ -25,7 +25,21 @@ class OrderItem extends Model
|
||||
|
||||
protected $table = 'order_items';
|
||||
|
||||
// NOTE: `created_by` is deliberately NOT fillable — accepting it via
|
||||
/**
|
||||
* The inventory model classes that can legally appear as the
|
||||
* polymorphic `item` on an OrderItem. Referenced from the item_type
|
||||
* validation rule and from the backfill / reconcile migrations,
|
||||
* which each iterate a filtered subset of this list.
|
||||
*/
|
||||
public const ITEM_TYPES = [
|
||||
Accessory::class,
|
||||
Consumable::class,
|
||||
Component::class,
|
||||
Asset::class,
|
||||
License::class,
|
||||
];
|
||||
|
||||
// NOTE: `created_by` is deliberately NOT fillable, accepting it via
|
||||
// mass-assignment would let a request attribute a line to any
|
||||
// user_id the caller writes. All internal writers set it via
|
||||
// explicit `$line->created_by = auth()->id()`.
|
||||
@ -42,6 +56,18 @@ class OrderItem extends Model
|
||||
'price' => 'decimal:4',
|
||||
];
|
||||
|
||||
/**
|
||||
* Static rules that don't depend on runtime data. The `item_type`
|
||||
* rule is composed from ITEM_TYPES in getRules() below so the
|
||||
* polymorphic class list has one source of truth.
|
||||
*/
|
||||
public $rules = [
|
||||
'order_id' => 'required|integer|exists:orders,id',
|
||||
'item_id' => 'required|integer|min:1',
|
||||
'qty' => 'required|integer|min:1',
|
||||
'price' => 'nullable|numeric|gte:0|max:99999999999999999.99',
|
||||
];
|
||||
|
||||
/**
|
||||
* Validation for the polymorphic pivot. item_id can't be validated
|
||||
* exists-style without a custom rule (the target table is a runtime
|
||||
@ -50,13 +76,12 @@ class OrderItem extends Model
|
||||
* item_id to caller sanity. order_id blocks reference to a deleted
|
||||
* Order via the exists rule (ignoring soft-deleted rows).
|
||||
*/
|
||||
public $rules = [
|
||||
'order_id' => 'required|integer|exists:orders,id',
|
||||
'item_type' => 'required|string|in:App\\Models\\Accessory,App\\Models\\Consumable,App\\Models\\Component,App\\Models\\Asset,App\\Models\\License',
|
||||
'item_id' => 'required|integer|min:1',
|
||||
'qty' => 'required|integer|min:1',
|
||||
'price' => 'nullable|numeric|gte:0|max:99999999999999999.99',
|
||||
];
|
||||
public function getRules(): array
|
||||
{
|
||||
return $this->rules + [
|
||||
'item_type' => 'required|string|in:'.implode(',', self::ITEM_TYPES),
|
||||
];
|
||||
}
|
||||
|
||||
public function order(): BelongsTo
|
||||
{
|
||||
|
||||
@ -7,7 +7,6 @@ use App\Models\Actionlog;
|
||||
|
||||
class AccessoryObserver
|
||||
{
|
||||
|
||||
/**
|
||||
* Listen to the User created event.
|
||||
*
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Models\License;
|
||||
use App\Models\OrderItem;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
@ -20,7 +22,7 @@ use Illuminate\Support\Facades\Schema;
|
||||
* recur across different suppliers or across companies in FMCS installs,
|
||||
* and collapsing them would rewrite history.
|
||||
*
|
||||
* Rows with a null / empty order_number are skipped — they represent
|
||||
* Rows with a null / empty order_number are skipped: they represent
|
||||
* inventory rows never associated with any order, and there's nothing
|
||||
* for the Orders table to record.
|
||||
*
|
||||
@ -29,19 +31,6 @@ use Illuminate\Support\Facades\Schema;
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Model class => source table name. Order determines the sequence
|
||||
* OrderItem rows are created in (purely cosmetic — a chronological
|
||||
* report would sort by Order.purchase_date or Order.created_at
|
||||
* anyway).
|
||||
*/
|
||||
private const SOURCES = [
|
||||
\App\Models\Accessory::class => 'accessories',
|
||||
\App\Models\Consumable::class => 'consumables',
|
||||
\App\Models\Component::class => 'components',
|
||||
\App\Models\Asset::class => 'assets',
|
||||
];
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
// Order dedupe cache: (order_number|supplier_id|company_id) => order_id.
|
||||
@ -52,13 +41,23 @@ return new class extends Migration
|
||||
// inventory tables didn't have their own currency column, and
|
||||
// stamping every historical Order with today's system
|
||||
// default_currency would fabricate information we don't
|
||||
// actually have — orders placed years ago may have been in a
|
||||
// actually have, orders placed years ago may have been in a
|
||||
// different currency than the install's current setting.
|
||||
// Downstream display code can fall back to
|
||||
// $snipeSettings->default_currency at render time (matching how
|
||||
// the pre-Orders info panels rendered purchase_cost anyway).
|
||||
//
|
||||
// Order iteration is purely cosmetic: a chronological report
|
||||
// would sort by Order.purchase_date or Order.created_at.
|
||||
//
|
||||
// License is filtered out because per-seat product-key
|
||||
// semantics need their own design pass before License can join
|
||||
// the Orders flow cleanly.
|
||||
$sourceClasses = array_diff(OrderItem::ITEM_TYPES, [License::class]);
|
||||
|
||||
foreach ($sourceClasses as $modelClass) {
|
||||
$table = (new $modelClass)->getTable();
|
||||
|
||||
foreach (self::SOURCES as $modelClass => $table) {
|
||||
// Defensive skip: if a source table has already had its
|
||||
// order_number column dropped (partial rerun, hand rollback,
|
||||
// schema drift), there's nothing to backfill from and the
|
||||
@ -129,10 +128,11 @@ return new class extends Migration
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
// Reversible only in a coarse sense — the drop wipes every row
|
||||
// Reversible only in a coarse sense: the delete wipes every row
|
||||
// in both tables rather than trying to reconstruct the original
|
||||
// per-source-column state. Combined with the follow-up
|
||||
// drop-columns migration (which does have a real down()), a full
|
||||
// rename-columns migration (143000, which does have a real
|
||||
// down() that restores the original column names), a full
|
||||
// rollback restores the pre-Orders layout.
|
||||
DB::table('order_items')->delete();
|
||||
DB::table('orders')->delete();
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ActionType;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\OrderItem;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@ -31,7 +31,13 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
foreach ([Accessory::class, Consumable::class, Component::class] as $modelClass) {
|
||||
// Filter OrderItem::ITEM_TYPES down to the models with a scalar
|
||||
// `qty` column that this reconciliation actually operates on.
|
||||
// Asset is 1:1 (no qty column, one row per unit), and License
|
||||
// is excluded per the note above.
|
||||
$qtyModels = array_diff(OrderItem::ITEM_TYPES, [Asset::class, License::class]);
|
||||
|
||||
foreach ($qtyModels as $modelClass) {
|
||||
$this->reconcileFor($modelClass);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user