diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 0ebfcbafdaaa..168a6bbc0595 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -225,6 +225,7 @@ class InvoiceController extends BaseController $invoice = $invoice->service() ->fillDefaults() ->triggeredActions($request) + ->adjustInventory() ->save(); event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); @@ -414,9 +415,14 @@ class InvoiceController extends BaseController return response()->json(['message' => ctrans('texts.locked_invoice')], 403); } + $old_invoice = $invoice->line_items; + $invoice = $this->invoice_repo->save($request->all(), $invoice); - - $invoice->service()->triggeredActions($request)->touchPdf(); + + $invoice->service() + ->triggeredActions($request) + ->touchPdf() + ->adjustInventory($old_invoice); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); diff --git a/app/Http/Livewire/BillingPortalPurchase.php b/app/Http/Livewire/BillingPortalPurchase.php index 4a5fd2f714ad..d383ee55220d 100644 --- a/app/Http/Livewire/BillingPortalPurchase.php +++ b/app/Http/Livewire/BillingPortalPurchase.php @@ -362,6 +362,7 @@ class BillingPortalPurchase extends Component ->service() ->markSent() ->fillDefaults() + ->adjustInventory() ->save(); Cache::put($this->hash, [ diff --git a/app/Jobs/Inventory/AdjustProductInventory.php b/app/Jobs/Inventory/AdjustProductInventory.php index 0c8468e19e27..7af22ce891b2 100644 --- a/app/Jobs/Inventory/AdjustProductInventory.php +++ b/app/Jobs/Inventory/AdjustProductInventory.php @@ -37,9 +37,8 @@ class AdjustProductInventory implements ShouldQueue public array $old_invoice; - public function __construct(Company $company, Invoice $invoice, array $old_invoice = []) + public function __construct(Company $company, Invoice $invoice, ?array $old_invoice = []) { - $this->company = $company; $this->invoice = $invoice; $this->old_invoice = $old_invoice; @@ -55,8 +54,10 @@ class AdjustProductInventory implements ShouldQueue { MultiDB::setDb($this->company->db); +nlog("old invoice count = " . count($this->old_invoice)); + if(count($this->old_invoice) > 0) - return $this->existingInventoryAdjustment(); + $this->existingInventoryAdjustment(); return $this->newInventoryAdjustment(); @@ -73,6 +74,8 @@ class AdjustProductInventory implements ShouldQueue $line_items = $this->invoice->line_items; +nlog($line_items); + foreach($line_items as $item) { @@ -81,10 +84,14 @@ class AdjustProductInventory implements ShouldQueue if(!$p) continue; - $p->in_stock_quantity -= $item->quantity; - $p->save(); - //check thresholds and notify user +nlog("subtracting back " . $item->quantity); + + $p->in_stock_quantity -= $item->quantity; + $p->saveQuietly(); + +nlog($p->toArray()); + if($p->stock_notification_threshold && $p->in_stock_quantity <= $p->stock_notification_threshold) $this->notifyStockLevels($p, 'product'); @@ -98,16 +105,18 @@ class AdjustProductInventory implements ShouldQueue private function existingInventoryAdjustment() { - foreach($this->old_invoice['line_items'] as $item) + foreach($this->old_invoice as $item) { - $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->where('in_stock_quantity', '>', 0)->first(); + $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->first(); if(!$p) continue; - $p->in_stock_quantity += $item->quantity; - $p->save(); +nlog("adding back " . $item->quantity); + $p->in_stock_quantity += $item->quantity; + $p->saveQuietly(); +nlog($p->toArray()); } } diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php index b5ef1d7d20bf..b3bf0a53ddb3 100644 --- a/app/Jobs/RecurringInvoice/SendRecurring.php +++ b/app/Jobs/RecurringInvoice/SendRecurring.php @@ -87,6 +87,7 @@ class SendRecurring implements ShouldQueue ->applyNumber() //->createInvitations() //need to only link invitations to those in the recurring invoice ->fillDefaults() + ->adjustInventory() ->save(); } diff --git a/app/Mail/Admin/InventoryNotificationObject.php b/app/Mail/Admin/InventoryNotificationObject.php index d7217b5ecd77..e752c5acf909 100644 --- a/app/Mail/Admin/InventoryNotificationObject.php +++ b/app/Mail/Admin/InventoryNotificationObject.php @@ -33,7 +33,7 @@ class InventoryNotificationObject public function __construct(Product $product, string $notification_level) { - $this->payment = $product; + $this->product = $product; $this->company = $product->company; $this->settings = $this->company->settings; } diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index c13673df2d51..a0a90deb0363 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -565,10 +565,11 @@ class InvoiceService return $this; } - public function adjustInventory() + public function adjustInventory($old_invoice = []) { + if($this->invoice->company->track_inventory) - AdjustProductInventory::dispatch($this->invoice->company, $this->invoice, null)->delay(rand(1,2)); + AdjustProductInventory::dispatchNow($this->invoice->company, $this->invoice, $old_invoice); return $this; } diff --git a/app/Services/Quote/ConvertQuote.php b/app/Services/Quote/ConvertQuote.php index 5f9d6fccac6f..eedb94bc7184 100644 --- a/app/Services/Quote/ConvertQuote.php +++ b/app/Services/Quote/ConvertQuote.php @@ -70,6 +70,7 @@ class ConvertQuote $invoice->service() ->fillDefaults() + ->adjustInventory() ->save(); $quote->invoice_id = $invoice->id; diff --git a/tests/Feature/Inventory/InventoryManagementTest.php b/tests/Feature/Inventory/InventoryManagementTest.php new file mode 100644 index 000000000000..8415e6cdc29a --- /dev/null +++ b/tests/Feature/Inventory/InventoryManagementTest.php @@ -0,0 +1,111 @@ +makeTestData(); + + $this->withoutMiddleware( + ThrottleRequests::class + ); + } + + public function testInventoryMovements() + { + + $product = Product::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'in_stock_quantity' => 100, + 'stock_notification' => true, + 'stock_notification_threshold' => 99 + ]); + + $invoice = Invoice::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'client_id' => $this->client->id + ]); + + $invoice->company->track_inventory = true; + $invoice->push(); + + + $invoice_item = new InvoiceItem; + $invoice_item->type_id = 1; + $invoice_item->product_key = $product->product_key; + $invoice_item->notes = $product->notes; + $invoice_item->quantity = 10; + $invoice_item->cost = 100; + + $line_items[] = $invoice_item; + $invoice->line_items = $line_items; + $invoice->number = Str::random(16); + + $invoice->client_id = $this->client->hashed_id; + + $invoice_array = $invoice->toArray(); + $invoice_array['client_id'] = $this->client->hashed_id; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/invoices/', $invoice_array) + ->assertStatus(200); + + $product = $product->refresh(); + + $this->assertEquals(90, $product->in_stock_quantity); + + + $arr = $response->json(); + $invoice_hashed_id = $arr['data']['id']; + + $invoice_item = new InvoiceItem; + $invoice_item->type_id = 1; + $invoice_item->product_key = $product->product_key; + $invoice_item->notes = $product->notes; + $invoice_item->quantity = 5; + $invoice_item->cost = 100; + + $line_items2[] = $invoice_item; + $invoice->line_items = $line_items2; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/invoices/'.$invoice_hashed_id, $invoice->toArray()) + ->assertStatus(200); + + $product = $product->refresh(); + + $this->assertEquals(95, $product->in_stock_quantity); + } +} diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 1e8cf07e92af..1cb37e72733a 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -217,6 +217,7 @@ trait MockAccountData $settings->timezone_id = '1'; $settings->entity_send_time = 0; + $this->company->track_inventory = true; $this->company->settings = $settings; $this->company->save();