From 42d9b7c1002250e1e19ef6cf54b4bb0940118adc Mon Sep 17 00:00:00 2001 From: blair2004 Date: Sat, 10 Dec 2022 10:17:14 +0100 Subject: [PATCH 1/3] Extends ResetCommand --- app/Console/Commands/ResetCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Console/Commands/ResetCommand.php b/app/Console/Commands/ResetCommand.php index ac662f7d3..ca68a8ff3 100644 --- a/app/Console/Commands/ResetCommand.php +++ b/app/Console/Commands/ResetCommand.php @@ -15,7 +15,7 @@ class ResetCommand extends Command * * @var string */ - protected $signature = 'ns:reset {--mode=soft} {--user=default}'; + protected $signature = 'ns:reset {--mode=soft} {--user=default} {--with-sales} {--with-procurements}'; /** * The console command description. @@ -70,8 +70,8 @@ public function handle() $this->initializeRole(); $this->demoService->run([ 'mode' => 'grocery', - 'create_sales' => true, - 'create_procurements' => true, + 'create_sales' => $this->option( 'with-sales' ) && $this->option( 'with-procurements' ) ? true : false, + 'create_procurements' => $this->option( 'with-procurements' ) ? true : false, ]); $this->info( __( 'The demo has been enabled.' ) ); break; From 7c648ecda42580fa71b14557d4c9ff6d3bff995a Mon Sep 17 00:00:00 2001 From: blair2004 Date: Thu, 22 Dec 2022 12:33:22 +0100 Subject: [PATCH 2/3] Changelog - Fixed: blair2004/NexoPOS#1220 - Added: hook for product on default receipt - Added: dependency check for providers - Added: improved options saving --- app/Models/Provider.php | 12 +- app/Services/DemoCoreService.php | 2 +- app/Services/Options.php | 80 ++++-------- app/Services/OrdersService.php | 14 ++- app/Services/ProviderService.php | 15 ++- app/Services/Setup.php | 2 + config/nexopos.php | 2 +- ...4_update_create_options_for_order_type.php | 26 ---- .../orders/templates/_product-name.blade.php | 3 + .../orders/templates/_receipt.blade.php | 7 +- tests/Traits/WithOrderTest.php | 118 +++++++++++++++--- 11 files changed, 171 insertions(+), 110 deletions(-) delete mode 100644 database/migrations/create/2021_11_17_215824_update_create_options_for_order_type.php create mode 100644 resources/views/pages/dashboard/orders/templates/_product-name.blade.php diff --git a/app/Models/Provider.php b/app/Models/Provider.php index c643c4042..2869b0e1d 100644 --- a/app/Models/Provider.php +++ b/app/Models/Provider.php @@ -2,16 +2,26 @@ namespace App\Models; +use App\Traits\NsDependable; use Illuminate\Database\Eloquent\Factories\HasFactory; class Provider extends NsModel { - use HasFactory; + use HasFactory, NsDependable; protected $table = 'nexopos_' . 'providers'; protected $guarded = []; + protected $isDependencyFor = [ + Procurement::class => [ + 'local_name' => 'name', + 'local_index' => 'id', + 'foreign_name' => 'code', + 'foreign_index' => 'provider_id', + ], + ]; + public function procurements() { return $this->hasMany( Procurement::class ); diff --git a/app/Services/DemoCoreService.php b/app/Services/DemoCoreService.php index 3692b3cf9..5cdbfabbc 100644 --- a/app/Services/DemoCoreService.php +++ b/app/Services/DemoCoreService.php @@ -134,7 +134,7 @@ public function createAccountingAccounts() 'account' => '008', ]); - ns()->option->set( 'ns_customer_debitting_cashflow_account', AccountType::account( '007' )->first()->id ); + ns()->option->set( 'ns_customer_debitting_cashflow_account', AccountType::account( '008' )->first()->id ); } public function createCustomers() diff --git a/app/Services/Options.php b/app/Services/Options.php index 2b56070a9..4abb38c66 100644 --- a/app/Services/Options.php +++ b/app/Services/Options.php @@ -44,10 +44,12 @@ public function setDefault( $options = [] ): void { Option::truncate(); + $types = app()->make( OrdersService::class )->getTypeLabels(); + $defaultOptions = [ 'ns_registration_enabled' => false, 'ns_store_name' => 'NexoPOS 4.x', - 'ns_pos_order_types' => [ 'takeaway', 'delivery' ], + 'ns_pos_order_types' => array_keys( $types ), ]; $options = array_merge( $defaultOptions, $options ); @@ -103,38 +105,7 @@ public function set( $key, $value, $expiration = null ) * it will save the new value and update * the option object. */ - $foundOption = collect( $this->rawOptions )->map( function( $option, $index ) use ( $value, $key, $expiration ) { - if ( $key === $index ) { - $this->hasFound = true; - - switch ( $value ) { - case is_array( $value ) : - $option->value = json_encode( $value ); - break; - case empty( $value ) && ! (bool) preg_match( '/[0-9]{1,}/', $value ) : - $option->value = ''; - break; - default: - $option->value = $value; - break; - } - - $option->expire_on = $expiration; - - /** - * this should be overridable - * from a user option or any - * extending this class - */ - $option = $this->beforeSave( $option ); - $option->save(); - - return $option; - } - - return false; - }) - ->filter(); + $foundOption = collect( $this->rawOptions )->filter( fn( $option, $index ) => $index === $key ); /** * if the option hasn't been found @@ -143,33 +114,30 @@ public function set( $key, $value, $expiration = null ) */ if ( $foundOption->isEmpty() ) { $option = new Option; - $option->key = trim( strtolower( $key ) ); - $option->array = false; - - switch ( $value ) { - case is_array( $value ) : - $option->value = json_encode( $value ); - break; - case empty( $value ) && ! (bool) preg_match( '/[0-9]{1,}/', $value ) : - $option->value = ''; - break; - default: - $option->value = $value; - break; - } + } else { + $option = $foundOption->first(); + } - $option->expire_on = $expiration; + $option->key = trim( strtolower( $key ) ); + $option->array = false; - /** - * this should be overridable - * from a user option or any - * extending this class - */ - $option = $this->beforeSave( $option ); - $option->save(); + if ( is_array( $value ) ) { + $option->value = json_encode( $value ); + } else if ( empty( $value ) && ! (bool) preg_match( '/[0-9]{1,}/', $value ) ) { + $option->value = ''; } else { - $option = $foundOption->first(); + $option->value = $value; } + + $option->expire_on = $expiration; + + /** + * this should be overridable + * from a user option or any + * extending this class + */ + $option = $this->beforeSave( $option ); + $option->save(); /** * Let's save the new option diff --git a/app/Services/OrdersService.php b/app/Services/OrdersService.php index ed4992430..38486d3d2 100644 --- a/app/Services/OrdersService.php +++ b/app/Services/OrdersService.php @@ -1032,12 +1032,16 @@ private function __saveOrderProducts($order, $products) $gross = 0; $orderProducts = $products->map(function ($product) use (&$subTotal, &$taxes, &$order, &$gross) { + + $previousQuantity = 0; + /** * if the product id is provided * then we can use that id as a reference. */ if ( isset( $product[ 'id' ] ) ) { $orderProduct = OrderProduct::find( $product[ 'id' ] ); + $previousQuantity = $orderProduct->quantity; } else { $orderProduct = new OrderProduct; } @@ -1113,7 +1117,15 @@ private function __saveOrderProducts($order, $products) 'total_price' => $orderProduct->total_price, ]; - $this->productService->stockAdjustment( ProductHistory::ACTION_SOLD, $history ); + /** + * __deleteUntrackedProducts will delete all products that + * already exists and which are edited. We'll here only records + * products that doesn't exists yet. + */ + if ( $orderProduct->wasRecentlyCreated ) { + $this->productService->stockAdjustment( ProductHistory::ACTION_SOLD, $history ); + } + } event( new OrderProductAfterSavedEvent( $orderProduct, $order, $product ) ); diff --git a/app/Services/ProviderService.php b/app/Services/ProviderService.php index 1889bb030..32364940a 100644 --- a/app/Services/ProviderService.php +++ b/app/Services/ProviderService.php @@ -189,14 +189,17 @@ public function cancelPaymentForProcurement( Procurement $procurement ) { $provider = Provider::find( $procurement->provider_id ); - if ( $procurement->payment_status === 'paid' ) { - $provider->amount_paid -= $procurement->cost; - } else { - $provider->amount_due -= $procurement->cost; + if ( $provider instanceof Provider ) { + + if ( $procurement->payment_status === 'paid' ) { + $provider->amount_paid -= $procurement->cost; + } else { + $provider->amount_due -= $procurement->cost; + } + + $provider->save(); } - $provider->save(); - return [ 'status' => 'succecss', 'message' => __( 'The procurement payment has been deducted.' ), diff --git a/app/Services/Setup.php b/app/Services/Setup.php index 7df37f861..ee8400d1c 100644 --- a/app/Services/Setup.php +++ b/app/Services/Setup.php @@ -14,6 +14,8 @@ class Setup { + public Options $options; + /** * Attempt database and save db informations * diff --git a/config/nexopos.php b/config/nexopos.php index 4bdf87ec8..0d24197fd 100644 --- a/config/nexopos.php +++ b/config/nexopos.php @@ -1,7 +1,7 @@ '4.8.10', + 'version' => '4.8.11', 'languages' => [ 'en' => 'English', 'fr' => 'Français', diff --git a/database/migrations/create/2021_11_17_215824_update_create_options_for_order_type.php b/database/migrations/create/2021_11_17_215824_update_create_options_for_order_type.php deleted file mode 100644 index 6a000eb15..000000000 --- a/database/migrations/create/2021_11_17_215824_update_create_options_for_order_type.php +++ /dev/null @@ -1,26 +0,0 @@ -option->set( 'ns_pos_order_types', [ 'takeaway', 'delivery' ]); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - // - } -}; diff --git a/resources/views/pages/dashboard/orders/templates/_product-name.blade.php b/resources/views/pages/dashboard/orders/templates/_product-name.blade.php new file mode 100644 index 000000000..cbfe33998 --- /dev/null +++ b/resources/views/pages/dashboard/orders/templates/_product-name.blade.php @@ -0,0 +1,3 @@ +{{ $product->name }} (x{{ $product->quantity }}) +
+{{ $product->unit->name }} \ No newline at end of file diff --git a/resources/views/pages/dashboard/orders/templates/_receipt.blade.php b/resources/views/pages/dashboard/orders/templates/_receipt.blade.php index 590485c37..6d2a302dd 100644 --- a/resources/views/pages/dashboard/orders/templates/_receipt.blade.php +++ b/resources/views/pages/dashboard/orders/templates/_receipt.blade.php @@ -1,6 +1,8 @@
@@ -33,9 +35,8 @@ @foreach( Hook::filter( 'ns-receipt-products', $order->combinedProducts ) as $product ) - {{ $product->name }} (x{{ $product->quantity }}) -
- {{ $product->unit->name }} + + render(), $product );?> {{ ns()->currency->define( $product->total_price ) }} diff --git a/tests/Traits/WithOrderTest.php b/tests/Traits/WithOrderTest.php index 00496924e..9d074f7a6 100644 --- a/tests/Traits/WithOrderTest.php +++ b/tests/Traits/WithOrderTest.php @@ -1580,7 +1580,6 @@ public function attemptCreateOrderWithInstalment() ->json( 'POST', 'api/nexopos/v4/orders', [ 'customer_id' => $customer->id, 'type' => [ 'identifier' => 'takeaway' ], - // 'discount_type' => 'percentage', 'discount_percentage' => $discountRate, 'discount_type' => 'flat', 'discount' => $discountValue, @@ -1800,25 +1799,32 @@ protected function attemptCreatePartiallyPaidOrderWithAdjustment() $responseData = json_decode( $response->getContent(), true ); /** - * performing the adjustment by increasing the quantity - * that is added to the order. + * Test 1: Testing Product History + * We should test if the records + * for partially paid order was created */ - $product = Product::with( 'unit_quantities' )->find(1); + foreach( $products as $product ) { + $history = ProductHistory::where( 'product_id', $product[ 'product_id' ] ) + ->where( 'operation_type', ProductHistory::ACTION_SOLD ) + ->where( 'order_id', $responseData[ 'data' ][ 'order' ][ 'id' ] ) + ->first(); + + $this->assertTrue( $history instanceof ProductHistory, 'No product history was created after placing the order with partial payment.' ); + $this->assertTrue( ( float ) $history->quantity === ( float ) $product[ 'quantity' ], 'The quantity of the product doesn\'t match the product history quantity.' ); + } + + /** + * Step 2: We'll here increase the + * quantity of the product attached to the order. + */ + $newProducts = $responseData[ 'data' ][ 'order' ][ 'products' ]; + Arr::set( $newProducts, '0.quantity', $newProducts[0][ 'quantity' ] + 1 ); - $responseData[ 'data' ][ 'order' ][ 'products' ][0][ 'quantity' ]++; $shippingFees = 150; $discountRate = 3.5; - $products = [ - [ - 'product_id' => $product->id, - 'quantity' => 5, - 'unit_price' => $product->unit_quantities[0]->sale_price, - 'unit_quantity_id' => $product->unit_quantities[0]->id, - ], - ]; - $subtotal = collect( $products )->map( fn( $product ) => $product[ 'unit_price' ] * $product[ 'quantity' ] )->sum(); + $subtotal = collect( $responseData[ 'data' ][ 'order' ][ 'products' ] )->map( fn( $product ) => $product[ 'unit_price' ] * $product[ 'quantity' ] )->sum(); $response = $this->withSession( $this->app[ 'session' ]->all() ) ->json( 'PUT', 'api/nexopos/v4/orders/' . $responseData[ 'data' ][ 'order' ][ 'id' ], [ 'customer_id' => 1, @@ -1839,7 +1845,7 @@ protected function attemptCreatePartiallyPaidOrderWithAdjustment() ], 'subtotal' => $subtotal, 'shipping' => $shippingFees, - 'products' => $responseData[ 'data' ][ 'order' ][ 'products' ], + 'products' => $newProducts, 'payments' => [], ]); @@ -1848,6 +1854,88 @@ protected function attemptCreatePartiallyPaidOrderWithAdjustment() ]); $response->assertStatus(200); + $json = $response->json(); + + /** + * Test 2: Testing Product History + * We should test if the records + * for partially paid order was created + */ + foreach( $products as $product ) { + $historyActionAdjustmentSale = ProductHistory::where( 'product_id', $product[ 'product_id' ] ) + ->where( 'operation_type', ProductHistory::ACTION_ADJUSTMENT_SALE ) + ->where( 'order_id', $responseData[ 'data' ][ 'order' ][ 'id' ] ) + ->first(); + + $this->assertTrue( $historyActionAdjustmentSale instanceof ProductHistory, 'The created history doesn\'t match what should have been created after an order modification.' ); + $this->assertSame( + ( float ) $historyActionAdjustmentSale->quantity, + ( float ) $json[ 'data' ][ 'order' ][ 'products' ][0][ 'quantity' ] - ( float ) $responseData[ 'data' ][ 'order' ][ 'products' ][0][ 'quantity' ], + 'The quantity of the product doesn\'t match the new product quantity after the order modfiication.' + ); + } + + /** + * Step 3: We'll here decrease the + * quantity of the product attached to the order. + */ + $newProducts = $responseData[ 'data' ][ 'order' ][ 'products' ]; + Arr::set( $newProducts, '0.quantity', $newProducts[0][ 'quantity' ] - 2 ); + + + $shippingFees = 150; + $discountRate = 3.5; + + $subtotal = collect( $responseData[ 'data' ][ 'order' ][ 'products' ] )->map( fn( $product ) => $product[ 'unit_price' ] * $product[ 'quantity' ] )->sum(); + $response = $this->withSession( $this->app[ 'session' ]->all() ) + ->json( 'PUT', 'api/nexopos/v4/orders/' . $responseData[ 'data' ][ 'order' ][ 'id' ], [ + 'customer_id' => 1, + 'type' => [ 'identifier' => 'takeaway' ], + 'discount_type' => 'percentage', + 'discount_percentage' => $discountRate, + 'addresses' => [ + 'shipping' => [ + 'name' => 'First Name Delivery', + 'surname' => 'Surname', + 'country' => 'Cameroon', + ], + 'billing' => [ + 'name' => 'EBENE Voundi', + 'surname' => 'Antony Hervé', + 'country' => 'United State Seattle', + ], + ], + 'subtotal' => $subtotal, + 'shipping' => $shippingFees, + 'products' => $newProducts, + 'payments' => [], + ]); + + $response->assertJson([ + 'status' => 'success', + ]); + + $response->assertStatus(200); + $json2 = $response->json(); + + /** + * Test 3: Testing Product History + * We should test if the records + * for partially paid order was created + */ + foreach( $products as $product ) { + $historyActionAdjustmentSale = ProductHistory::where( 'product_id', $product[ 'product_id' ] ) + ->where( 'operation_type', ProductHistory::ACTION_ADJUSTMENT_RETURN ) + ->where( 'order_id', $responseData[ 'data' ][ 'order' ][ 'id' ] ) + ->first(); + + $this->assertTrue( $historyActionAdjustmentSale instanceof ProductHistory, 'The created history doesn\'t match what should have been created after an order modification.' ); + $this->assertSame( + ( float ) $historyActionAdjustmentSale->quantity, + ( float ) $json[ 'data' ][ 'order' ][ 'products' ][0][ 'quantity' ] - ( float ) $json2[ 'data' ][ 'order' ][ 'products' ][0][ 'quantity' ], + 'The quantity of the product doesn\'t match the new product quantity after the order modfiication.' + ); + } } protected function attemptTestRewardSystem() From c6fe5fbd8693f3afb84362e029ebdbb8310ef3b3 Mon Sep 17 00:00:00 2001 From: blair2004 Date: Thu, 22 Dec 2022 14:22:09 +0100 Subject: [PATCH 3/3] Changelog: - Added: cleaning symbolic links. --- app/Console/Commands/ModuleSymlinkCommand.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/Console/Commands/ModuleSymlinkCommand.php b/app/Console/Commands/ModuleSymlinkCommand.php index 00fab0a49..5e226faf3 100644 --- a/app/Console/Commands/ModuleSymlinkCommand.php +++ b/app/Console/Commands/ModuleSymlinkCommand.php @@ -4,6 +4,7 @@ use App\Services\ModulesService; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Storage; class ModuleSymlinkCommand extends Command { @@ -36,6 +37,12 @@ public function handle() } else { $modules = $moduleService->get(); + /** + * let's clear all existing links + */ + Storage::disk( 'ns' )->deleteDirectory( 'public/modules' ); + Storage::disk( 'ns' )->makeDirectory( 'public/modules' ); + $this->withProgressBar( $modules, function( $module ) use ( $moduleService ) { $moduleService->createSymLink( $module[ 'namespace' ] ); });