commit21
This commit is contained in:
@@ -21,6 +21,46 @@ use Illuminate\Validation\ValidationException;
|
||||
/** Lot quantities partition the physical ledger; they never create physical stock. */
|
||||
class InventoryLots
|
||||
{
|
||||
public function canIdentify(User $actor, ?SupplyRecord $metadata): bool
|
||||
{
|
||||
return $metadata !== null && $metadata->kind === 'lot' && $metadata->tenant_id === $actor->tenant_id
|
||||
&& $this->allows($actor, 'inventory_lot_prepare')
|
||||
&& app(SupplyPlanning::class)->latest('lot')->where('record_key', $metadata->record_key)->value('id') === $metadata->id;
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $context
|
||||
* @param array<string,mixed> $entry
|
||||
*/
|
||||
public function canDecideLot(User $actor, ?SupplyRecord $metadata, array $context, array $entry, string $action): bool
|
||||
{
|
||||
if ($metadata === null || $metadata->tenant_id !== $actor->tenant_id || $metadata->kind !== 'lot'
|
||||
|| ($entry['metadata_id'] ?? null) !== $metadata->id || ! ($context['reconciled'] ?? false)
|
||||
|| ($entry['quantity_milli'] ?? 0) <= 0 || ! in_array($action, ['hold', 'release'], true)
|
||||
|| ! $this->allows($actor, 'inventory_lot_'.$action)
|
||||
|| app(SupplyPlanning::class)->latest('lot')->where('record_key', $metadata->record_key)->value('id') !== $metadata->id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $action === 'hold' || (($entry['eligible'] ?? false) && ! in_array($actor->id, $entry['contributors'], true));
|
||||
}
|
||||
|
||||
private function independentSetupReviewer(User $actor, SupplyRecord $record): bool
|
||||
{
|
||||
return ! in_array($actor->id, $record->snapshot['contributors'], true);
|
||||
}
|
||||
|
||||
/** Participation only: activation still checks balance, active setup and metadata stamps. */
|
||||
public function canActivate(User $actor, SupplyRecord $record): bool
|
||||
{
|
||||
if ($record->kind !== 'lot_setup' || $record->state !== 'draft' || $record->tenant_id !== $actor->tenant_id
|
||||
|| ! $this->allows($actor, 'inventory_lot_review') || ! $this->independentSetupReviewer($actor, $record)) {
|
||||
return false;
|
||||
}
|
||||
$latest = app(SupplyPlanning::class)->latest('lot_setup')->where('record_key', $record->record_key)->first();
|
||||
|
||||
return $latest?->id === $record->id && $this->setup($record->stock_item_id, $record->warehouse_id)?->id === ($record->snapshot['previous_setup_id'] ?? null);
|
||||
}
|
||||
|
||||
public function allows(User $actor, string $permission): bool
|
||||
{
|
||||
return $actor->hasFunctions($permission === 'inventory_lot_read' ? [$permission, 'admin'] : [$permission])
|
||||
@@ -167,7 +207,7 @@ class InventoryLots
|
||||
}
|
||||
if ($v['action'] === 'activate') {
|
||||
$this->check($old?->kind === 'lot_setup' && $old->state === 'draft', 'action', 'Prepare primeiro a conferência do saldo.');
|
||||
$this->check(! in_array($actor->id, $old->snapshot['contributors'], true), 'action', 'A activação exige conferência independente da preparação.');
|
||||
$this->check($this->independentSetupReviewer($actor, $old), 'action', 'A activação exige conferência independente da preparação.');
|
||||
$active = $this->setup($old->stock_item_id, $old->warehouse_id);
|
||||
$this->check(($active?->id) === ($old->snapshot['previous_setup_id'] ?? null), 'action', 'A configuração activa mudou desde a preparação. Actualize a revisão.');
|
||||
$this->check(app(StockWorkflow::class)->balance($old->stock_item_id, $old->warehouse_id) === $old->snapshot['balance'], 'action', 'O saldo mudou. Actualize a preparação e confira novamente.');
|
||||
@@ -186,6 +226,7 @@ class InventoryLots
|
||||
}
|
||||
if ($v['action'] === 'identify') {
|
||||
$this->check($old?->kind === 'lot', 'action', 'Seleccione a identificação existente.');
|
||||
$this->check($this->canIdentify($actor, $old), 'action', 'A identificação exige competência e versão actual.');
|
||||
$metadata = $this->metadata($v['data']);
|
||||
|
||||
// A correction invalidates the previous quality release by metadata version.
|
||||
@@ -199,6 +240,7 @@ class InventoryLots
|
||||
$this->check($entry !== null && $context['reconciled'] && $entry['quantity_milli'] > 0, 'action', 'Lote sem saldo conferido neste armazém.');
|
||||
$state = app(SupplyPlanning::class)->latest('lot_state')->whereJsonContains('data->lot_id', $identity->id)->where('warehouse_id', $d['warehouse_id'])->first();
|
||||
$this->check((int) $d['state_version'] === ($state->version ?? 0) && $old?->id === $entry['metadata_id'], 'version', 'A identificação ou a decisão mudou. Actualize a página.');
|
||||
$this->check($this->canDecideLot($actor, $old, $context, $entry, $v['action']), 'action', 'A decisão exige saldo conferido, competência, identificação válida e independência na libertação.');
|
||||
if ($v['action'] === 'release') {
|
||||
$this->check(! in_array($actor->id, $entry['contributors'], true), 'action', 'A libertação exige pessoa independente da identificação e recepção.');
|
||||
$this->check($entry['eligible'], 'action', 'Confirme identificação e validade suficiente antes de libertar.');
|
||||
|
||||
Reference in New Issue
Block a user