Implementa regras de investimento por empresa com dupla aprovacao
This commit is contained in:
@@ -36,6 +36,7 @@ class PurchaseWorkflow
|
||||
$this->sourceApproved($requisition, $rules);
|
||||
$quote = Quotation::query()->where('requisition_id', $requisition->id)->lockForUpdate()->findOrFail($data['quotation_id']);
|
||||
$result = $this->checkComparison($user, $requisition, $quote);
|
||||
$investment = app(InvestmentWorkflow::class)->check($requisition, $quote);
|
||||
app(BudgetWorkflow::class)->record($user, $requisition, 'award', $quote->total_minor, $quote->subtotal_minor);
|
||||
$award = PurchaseAward::query()->create([
|
||||
'tenant_id' => $user->tenant_id, 'requisition_id' => $requisition->id, 'quotation_id' => $quote->id,
|
||||
@@ -43,8 +44,9 @@ class PurchaseWorkflow
|
||||
'delivery_location' => $data['delivery_location'], 'status' => 'pending',
|
||||
'subtotal_minor' => $quote->subtotal_minor, 'tax_minor' => $quote->tax_minor, 'total_minor' => $quote->total_minor,
|
||||
'approval_snapshot' => $rules, 'decision_matrix' => $result['rows'], 'comparison_hash' => $result['fingerprint'],
|
||||
'investment_snapshot' => $investment,
|
||||
]);
|
||||
$roles = $this->roles($rules, $rules['approval_includes_tax'] ? $quote->total_minor : $quote->subtotal_minor);
|
||||
$roles = $this->roles($rules, $rules['approval_includes_tax'] ? $quote->total_minor : $quote->subtotal_minor, $requisition->items()->where('purchase_type', 'equipment')->exists());
|
||||
foreach ($roles as $position => $role) {
|
||||
$award->approvals()->create(['tenant_id' => $user->tenant_id, 'position' => $position + 1, 'role' => $role]);
|
||||
}
|
||||
@@ -130,6 +132,8 @@ class PurchaseWorkflow
|
||||
$snapshot = [
|
||||
'development_only' => true, 'budget_checked' => false,
|
||||
'budget_control' => app(BudgetWorkflow::class)->record($user, $requisition, 'commit', $quote->total_minor, $quote->subtotal_minor),
|
||||
'investment_rules' => $award->investment_snapshot,
|
||||
'equipment_requests' => $requisition->items()->where('purchase_type', 'equipment')->get()->map(fn ($line) => $line->only(['id', 'description', 'production_area']))->all(),
|
||||
'company' => Tenant::query()->findOrFail($user->tenant_id)->only(['id', 'name']),
|
||||
'requisition' => $requisition->only(['id', 'number', 'justification']),
|
||||
'department' => $requisition->department->only(['id', 'name']),
|
||||
@@ -166,6 +170,9 @@ class PurchaseWorkflow
|
||||
$this->supportedItems($requisition);
|
||||
$this->sourceApproved($requisition, $rules);
|
||||
$quote = Quotation::query()->where('requisition_id', $requisition->id)->lockForUpdate()->findOrFail($award->quotation_id);
|
||||
if (app(InvestmentWorkflow::class)->check($requisition, $quote) !== $award->investment_snapshot) {
|
||||
throw ValidationException::withMessages(['investment' => 'A regra de investimento ou a base técnica/orçamental mudou. As aprovações anteriores não autorizam uma emissão com outra base.']);
|
||||
}
|
||||
$result = $this->checkComparison($user, $requisition, $quote);
|
||||
if ($result['fingerprint'] !== $award->comparison_hash || $award->approval_snapshot !== $rules ||
|
||||
$quote->subtotal_minor !== $award->subtotal_minor || $quote->tax_minor !== $award->tax_minor || $quote->total_minor !== $award->total_minor) {
|
||||
@@ -214,13 +221,13 @@ class PurchaseWorkflow
|
||||
/** @param array{first_limit_minor: int, second_limit_minor: int, approval_includes_tax: bool, rules_provisional: bool} $rules
|
||||
* @return list<string>
|
||||
*/
|
||||
private function roles(array $rules, int $amount): array
|
||||
private function roles(array $rules, int $amount, bool $equipment = false): array
|
||||
{
|
||||
$roles = ['department_head'];
|
||||
if ($amount > $rules['first_limit_minor']) {
|
||||
$roles[] = 'procurement';
|
||||
}
|
||||
if ($amount > $rules['second_limit_minor']) {
|
||||
if ($amount > $rules['second_limit_minor'] || $equipment) {
|
||||
$roles[] = 'finance';
|
||||
}
|
||||
|
||||
@@ -230,7 +237,7 @@ class PurchaseWorkflow
|
||||
/** @param array{first_limit_minor: int, second_limit_minor: int, approval_includes_tax: bool, rules_provisional: bool} $rules */
|
||||
private function sourceApproved(Requisition $requisition, array $rules): void
|
||||
{
|
||||
$roles = $this->roles($rules, $rules['approval_includes_tax'] ? $requisition->total_minor : $requisition->subtotal_minor);
|
||||
$roles = $this->roles($rules, $rules['approval_includes_tax'] ? $requisition->total_minor : $requisition->subtotal_minor, $requisition->items()->where('purchase_type', 'equipment')->exists());
|
||||
$steps = $requisition->steps()->get();
|
||||
$seen = [$requisition->requester_id];
|
||||
if ($requisition->status !== 'approved' || $steps->count() !== count($roles)) {
|
||||
@@ -248,7 +255,7 @@ class PurchaseWorkflow
|
||||
private function validateFinalApprovals(PurchaseAward $award, Requisition $requisition): void
|
||||
{
|
||||
$rules = $this->rules($requisition);
|
||||
$roles = $this->roles($rules, $rules['approval_includes_tax'] ? $award->total_minor : $award->subtotal_minor);
|
||||
$roles = $this->roles($rules, $rules['approval_includes_tax'] ? $award->total_minor : $award->subtotal_minor, $requisition->items()->where('purchase_type', 'equipment')->exists());
|
||||
$steps = $award->approvals()->get();
|
||||
$seen = [$requisition->requester_id, $award->created_by];
|
||||
if ($steps->count() !== count($roles)) {
|
||||
@@ -268,10 +275,10 @@ class PurchaseWorkflow
|
||||
private function supportedItems(Requisition $requisition): void
|
||||
{
|
||||
if ($requisition->items()->where('purchase_type', 'equipment')->exists()) {
|
||||
throw ValidationException::withMessages(['purchase_type' => 'O limite próprio de investimento e a sua regra de aplicação ainda não foram definidos pela empresa. A validação técnica e Finanças não dispensam essa decisão; adjudicação e emissão de equipamento bloqueadas.']);
|
||||
app(InvestmentWorkflow::class)->requireCurrent();
|
||||
}
|
||||
foreach ($requisition->items()->get() as $line) {
|
||||
if (! in_array($line->purchase_type, ['consumable', 'service', 'other', 'spare_part']) || ($line->purchase_type === 'spare_part' && (! $line->equipment_id || ! $line->spare_part_id))) {
|
||||
if (! in_array($line->purchase_type, ['consumable', 'service', 'other', 'spare_part', 'equipment']) || ($line->purchase_type === 'spare_part' && (! $line->equipment_id || ! $line->spare_part_id))) {
|
||||
throw ValidationException::withMessages(['purchase_type' => 'Tipo não suportado ou peça sem equipamento/artigo associado. Complete o cadastro antes da compra.']);
|
||||
}
|
||||
app(EquipmentWorkflow::class)->line($line->toArray(), $requisition->department_id, $requisition->priority);
|
||||
|
||||
Reference in New Issue
Block a user