diff --git a/code/app/AggregateBooking.php b/code/app/AggregateBooking.php index d0b0cc6d..f37bf9fd 100644 --- a/code/app/AggregateBooking.php +++ b/code/app/AggregateBooking.php @@ -66,27 +66,25 @@ public function getUpdatedAtAttribute() public function getStatusAttribute() { - foreach ($this->bookings as $booking) { - /* - Nota bene: in questo aggregato ci vanno sia le prenotazioni - effettivamente salvate sul database che le prenotazioni allocate - ma non realmente esistenti (ma che fungono da wrapper in molte - circostanze). - Lo stato dell'aggregato dipende solo da quelle reali: se una - prenotazioni vera risulta consegnata, ed una "virtuale" no - (quelle virtuali non lo sono mai, per definizione), comunque - tutto l'aggregato deve risultare consegnato - */ - if ($booking->exists && $booking->status != 'shipped') { - return $booking->status; - } + /* + Nota bene: in questo aggregato ci vanno sia le prenotazioni + effettivamente salvate sul database che le prenotazioni allocate + ma non realmente esistenti (ma che fungono da wrapper in molte + circostanze). + Lo stato dell'aggregato dipende solo da quelle reali: se una + prenotazioni vera risulta consegnata, ed una "virtuale" no + (quelle virtuali non lo sono mai, per definizione), comunque tutto + l'aggregato deve risultare consegnato + */ + $target = $this->bookings->filter(fn($b) => $b->exists && $b->status != 'shipped')->first(); + if ($target) { + return $target->status; } foreach ($this->bookings as $booking) { - foreach($booking->friends_bookings as $fbooking) { - if ($fbooking->exists && $fbooking->status != 'shipped') { - return $fbooking->status; - } + $target = $booking->friends_bookings->filter(fn($b) => $b->exists && $b->status != 'shipped')->first(); + if ($target) { + return $target->status; } } diff --git a/code/app/Helpers/Status.php b/code/app/Helpers/Status.php index b5442d8b..7835a026 100644 --- a/code/app/Helpers/Status.php +++ b/code/app/Helpers/Status.php @@ -74,10 +74,22 @@ public static function orders() public static function invoices() { return [ - 'pending' => _i('In Attesa'), - 'to_verify' => _i('Da Verificare'), - 'verified' => _i('Verificata'), - 'payed' => _i('Pagata'), + 'pending' => (object) [ + 'label' => _i('In Attesa'), + 'icon' => 'clock', + ], + 'to_verify' => (object) [ + 'label' => _i('Da Verificare'), + 'icon' => 'pin-angle', + ], + 'verified' => (object) [ + 'label' => _i('Verificata'), + 'icon' => 'search', + ], + 'payed' => (object) [ + 'label' => _i('Pagata'), + 'icon' => 'check', + ], ]; } } diff --git a/code/app/Http/Controllers/ReceiptsController.php b/code/app/Http/Controllers/ReceiptsController.php index 86d8096a..21fa8a1a 100644 --- a/code/app/Http/Controllers/ReceiptsController.php +++ b/code/app/Http/Controllers/ReceiptsController.php @@ -4,11 +4,7 @@ use Illuminate\Http\Request; -use Auth; -use DB; use PDF; -use Log; -use Mail; use App\Services\ReceiptsService; use App\Notifications\ReceiptForward; @@ -21,7 +17,7 @@ public function __construct(ReceiptsService $service) $this->middleware('auth'); $this->commonInit([ - 'reference_class' => 'App\\Receipt', + 'reference_class' => Receipt::class, 'service' => $service, ]); } @@ -36,11 +32,10 @@ public function index(Request $request) ]); } - public function show($id) + public function show(Request $request, $id) { $receipt = $this->service->show($id); - - $user = Auth::user(); + $user = $request->user(); if ($user->can('movements.admin', $user->gas)) { return view('receipt.edit', ['receipt' => $receipt]); @@ -123,6 +118,20 @@ private function outputCSV($elements) }); } + private function send($elements): void + { + foreach($elements as $receipt) { + if ($receipt->mailed == false) { + try { + $this->sendByMail($receipt); + } + catch(\Exception $e) { + \Log::error('Errore in inoltro ricevuta: ' . $e->getMessage()); + } + } + } + } + public function search(Request $request) { $start = decodeDate($request->input('startdate')); @@ -134,18 +143,8 @@ public function search(Request $request) switch($format) { case 'send': - foreach($elements as $receipt) { - if ($receipt->mailed == false) { - try { - $this->sendByMail($receipt); - } - catch(\Exception $e) { - \Log::error('Errore in inoltro ricevuta: ' . $e->getMessage()); - } - } - } - - $elements = $this->service->list($start, $end, $supplier_id); + $this->send($elements); + $elements = $this->service->list($start, $end, $supplier_id); /* Qui il break manca di proposito diff --git a/code/app/Models/Concerns/RoleableTrait.php b/code/app/Models/Concerns/RoleableTrait.php index 779ff4f5..27c8efde 100644 --- a/code/app/Models/Concerns/RoleableTrait.php +++ b/code/app/Models/Concerns/RoleableTrait.php @@ -80,7 +80,7 @@ public function targetsByAction($actions, $exclude_trashed = true) $action = trim($action); $class = classByRule($action); - $roles = $this->roles()->get()->filter(function($role) use ($action) { + $roles = $this->roles->filter(function($role) use ($action) { return $role->enabledAction($action); }); diff --git a/code/app/Services/InvoicesService.php b/code/app/Services/InvoicesService.php index d19d2fb3..9bc3f8f1 100644 --- a/code/app/Services/InvoicesService.php +++ b/code/app/Services/InvoicesService.php @@ -20,27 +20,24 @@ private function testAccess($supplier) public function list($start, $end, $supplier_id) { - if ($supplier_id) { - $supplier = Supplier::tFind($supplier_id); - } - else { - $supplier = null; - } - + $supplier = Supplier::withTrashed()->find($supplier_id); $user = $this->testAccess($supplier); - $query = Invoice::where(function($query) use($start, $end) { - $query->whereHas('payment', function($query) use($start, $end) { - $query->where('date', '>=', $start)->where('date', '<=', $end); - })->orWhereDoesntHave('payment'); - }); + $query = Invoice::whereBetween('date', [$start, $end]); if ($supplier) { $query->where('supplier_id', $supplier->id); } else { - $suppliers = $user->targetsByAction('movements.admin,supplier.orders,supplier.movements'); - $query->whereIn('supplier_id', array_keys($suppliers)); + if ($user->can('movements.admin', $user->gas)) { + $suppliers = Supplier::withTrashed()->pluck('id'); + } + else { + $suppliers = $user->targetsByAction('supplier.invoices,supplier.movements', false); + $suppliers = array_keys($suppliers); + } + + $query->whereIn('supplier_id', $suppliers); } $elements = $query->get(); @@ -107,7 +104,7 @@ public function update($id, array $request) private function initGlobalSummeries($invoice) { - $global_summary = (object)[ + $global_summary = (object) [ 'products' => [], 'total' => 0, 'total_taxable' => 0, @@ -205,7 +202,7 @@ private function movementAttach($type, $user, $invoice) public function saveMovements($id, $request) { $invoice = $this->show($id); - $this->ensureAuth(['movements.admin' => 'gas', 'supplier.movements' => $invoice->supplier]); + $user = $this->ensureAuth(['movements.admin' => 'gas', 'supplier.movements' => $invoice->supplier]); $invoice->deleteMovements(); @@ -239,6 +236,11 @@ public function saveMovements($id, $request) } } + /* + Il parametro $invoice->payment_id viene settato direttamente nella + callback di elaborazione del tipo movimento InvoicePayment + */ + $invoice->otherMovements()->sync($other_movements); } diff --git a/code/app/Services/UsersService.php b/code/app/Services/UsersService.php index c0df3f66..4413b8c0 100644 --- a/code/app/Services/UsersService.php +++ b/code/app/Services/UsersService.php @@ -47,15 +47,11 @@ public function list($term = '', $all = false) public function show($id) { - $user = Auth::user(); - if (is_null($user)) { - throw new AuthException(401); - } - $searched = User::withTrashed()->findOrFail($id); - if ($searched->testUserAccess() == false) + if ($searched->testUserAccess() == false) { $this->ensureAuth(['users.admin' => 'gas', 'users.view' => 'gas']); + } return $searched; } diff --git a/code/app/View/Icons/IconsMap.php b/code/app/View/Icons/IconsMap.php index 3700ad06..ad157ba1 100644 --- a/code/app/View/Icons/IconsMap.php +++ b/code/app/View/Icons/IconsMap.php @@ -12,6 +12,21 @@ public static function selective() return []; } + protected static function unrollStatuses($array, $statuses) + { + foreach($statuses as $identifier => $meta) { + $array[$meta->icon] = (object) [ + 'test' => function ($obj) use ($identifier) { + return $obj->status == $identifier; + }, + 'text' => $meta->label, + 'group' => 'status', + ]; + } + + return $array; + } + /* Questa funzione deve ritornare un array associativo che contiene la definizione di ogni icona prevista per la classe in oggetto. Le chiavi diff --git a/code/app/View/Icons/Invoice.php b/code/app/View/Icons/Invoice.php index 9841b1e2..59b28f2e 100644 --- a/code/app/View/Icons/Invoice.php +++ b/code/app/View/Icons/Invoice.php @@ -2,37 +2,12 @@ namespace App\View\Icons; +use App\Helpers\Status; + class Invoice extends IconsMap { public static function commons($user) { - $ret = [ - 'clock' => (object) [ - 'test' => function ($obj) { - return $obj->status == 'pending'; - }, - 'text' => _i('In Attesa'), - ], - 'pin-angle' => (object) [ - 'test' => function ($obj) { - return $obj->status == 'to_verify'; - }, - 'text' => _i('Da Verificare'), - ], - 'search' => (object) [ - 'test' => function ($obj) { - return $obj->status == 'verified'; - }, - 'text' => _i('Verificata'), - ], - 'check' => (object) [ - 'test' => function ($obj) { - return $obj->status == 'payed'; - }, - 'text' => _i('Pagata'), - ], - ]; - - return $ret; + return self::unrollStatuses([], Status::invoices()); } } diff --git a/code/app/View/Icons/Order.php b/code/app/View/Icons/Order.php index 1a84a063..0925ce3c 100644 --- a/code/app/View/Icons/Order.php +++ b/code/app/View/Icons/Order.php @@ -18,16 +18,7 @@ public static function commons($user) ] ]; - foreach(Status::orders() as $identifier => $meta) { - $ret[$meta->icon] = (object) [ - 'test' => function ($obj) use ($identifier) { - return $obj->status == $identifier; - }, - 'text' => $meta->label, - 'group' => 'status', - ]; - } - + $ret = self::unrollStatuses($ret, Status::orders()); return $ret; } } diff --git a/code/database/factories/ProductFactory.php b/code/database/factories/ProductFactory.php index c48c7c27..ec8825e0 100644 --- a/code/database/factories/ProductFactory.php +++ b/code/database/factories/ProductFactory.php @@ -14,7 +14,7 @@ public function definition() { return [ 'name' => $this->faker->text(10), - 'price' => $this->faker->randomNumber(2), + 'price' => $this->faker->randomNumber(2, true), ]; } } diff --git a/code/tests/Services/InvoicesServiceTest.php b/code/tests/Services/InvoicesServiceTest.php index 8024a972..5b6b47e5 100644 --- a/code/tests/Services/InvoicesServiceTest.php +++ b/code/tests/Services/InvoicesServiceTest.php @@ -7,6 +7,10 @@ use Illuminate\Database\Eloquent\ModelNotFoundException; use App\Exceptions\AuthException; +use App\User; +use App\Supplier; +use App\Order; +use App\Movement; class InvoicesServiceTest extends TestCase { @@ -16,13 +20,15 @@ public function setUp(): void { parent::setUp(); - $this->supplier = \App\Supplier::factory()->create(); + $this->supplier = Supplier::factory()->create(); $this->userWithAdminPerm = $this->createRoleAndUser($this->gas, 'movements.admin'); - $this->userWithNoPerms = \App\User::factory()->create(['gas_id' => $this->gas->id]); + $this->userWithSupplierPerm = $this->createRoleAndUser($this->gas, 'supplier.movements', $this->supplier); + $this->userWithInvoicesPerm = $this->createRoleAndUser($this->gas, 'supplier.invoices', $this->supplier); + $this->userWithNoPerms = User::factory()->create(['gas_id' => $this->gas->id]); } /* - Salvataggio Fattura con permessi sbagliati + Salvataggio Fattura con permessi sbagliati (utente) */ public function testFailsToStore() { @@ -37,15 +43,47 @@ public function testFailsToStore() ]); } - private function createInvoice() + /* + Salvataggio Fattura con permessi sbagliati (amministratore) + */ + public function testFailsToStoreAdmin() { + $this->expectException(AuthException::class); $this->actingAs($this->userWithAdminPerm); - $today = date('Y-m-d'); + + app()->make('InvoicesService')->store([ + 'number' => 'ABC123', + 'supplier_id' => $this->supplier->id, + 'date' => printableDate(date('Y-m-d')), + ]); + } + + /* + Salvataggio Fattura con permessi sbagliati (fornitore sbagliato) + */ + public function testFailsToStoreSupplier() + { + $this->expectException(AuthException::class); + $other_supplier = Supplier::factory()->create(); + $this->actingAs($this->userWithInvoicesPerm); + + $invoice = app()->make('InvoicesService')->store(array( + 'number' => 'ABC123', + 'supplier_id' => $other_supplier->id, + 'date' => printableDate(date('Y-m-d')), + )); + + return app()->make('InvoicesService')->show($invoice->id); + } + + private function createInvoice() + { + $this->actingAs($this->userWithInvoicesPerm); $invoice = app()->make('InvoicesService')->store(array( 'number' => 'ABC123', 'supplier_id' => $this->supplier->id, - 'date' => printableDate($today), + 'date' => printableDate(date('Y-m-d')), )); return app()->make('InvoicesService')->show($invoice->id); @@ -53,14 +91,15 @@ private function createInvoice() private function wireOrders($invoice) { - $order1 = \App\Order::factory()->create([ + $order1 = Order::factory()->create([ 'supplier_id' => $this->supplier->id, ]); - $order2 = \App\Order::factory()->create([ + $order2 = Order::factory()->create([ 'supplier_id' => $this->supplier->id, ]); + $this->actingAs($this->userWithInvoicesPerm); app()->make('InvoicesService')->wire($invoice->id, 'review', [ 'order_id' => [$order1->id, $order2->id] ]); @@ -100,7 +139,7 @@ public function testNoList() $past = date('Y-m-d', strtotime('-1 months')); $future = date('Y-m-d', strtotime('+10 years')); - app()->make('InvoicesService')->list($past, $future, 0); + app()->make('InvoicesService')->list($past, $future, '0'); } /* @@ -109,23 +148,23 @@ public function testNoList() public function testList() { $this->createInvoice(); - - $this->nextRound(); - $past = date('Y-m-d', strtotime('-1 months')); $future = date('Y-m-d', strtotime('+10 years')); - $invoices = app()->make('InvoicesService')->list($past, $future, '0'); - $this->assertEquals(1, $invoices->count()); - $this->nextRound(); + foreach([$this->userWithInvoicesPerm, $this->userWithSupplierPerm, $this->userWithAdminPerm] as $index => $user) { + $this->actingAs($user); - $invoices = app()->make('InvoicesService')->list($past, $future, '42'); - $this->assertEquals(0, $invoices->count()); + $this->nextRound(); + $user = app()->make('UsersService')->show($user->id); - $this->nextRound(); + $invoices = app()->make('InvoicesService')->list($past, $future, '0'); + $this->assertEquals(1, $invoices->count()); - $invoices = app()->make('InvoicesService')->list($past, $future, $this->supplier->id); - $this->assertEquals(1, $invoices->count()); + $this->nextRound(); + + $invoices = app()->make('InvoicesService')->list($past, $future, $this->supplier->id); + $this->assertEquals(1, $invoices->count()); + } } /* @@ -143,7 +182,7 @@ public function testContents() $this->nextRound(); - $this->actingAs($this->userWithAdminPerm); + $this->actingAs($this->userWithInvoicesPerm); app()->make('InvoicesService')->wire($invoice->id, 'review', [ 'order_id' => [$order1->id, $order2->id] @@ -156,7 +195,7 @@ public function testContents() } /* - Elenco Fatture pagate + Pagamento fattura */ public function testPayAndList() { @@ -173,7 +212,7 @@ public function testPayAndList() $future = date('Y-m-d', strtotime('-3 days')); $invoices = app()->make('InvoicesService')->list($past, $future, '0'); - $this->assertEquals(1, $invoices->count()); + $this->assertEquals(0, $invoices->count()); $this->nextRound(); @@ -181,20 +220,53 @@ public function testPayAndList() $this->nextRound(); - $movement = \App\Movement::generate('invoice-payment', $this->userWithAdminPerm->gas, $invoice, 10); - $movement->save(); + $this->actingAs($this->userWithSupplierPerm); - $invoices = app()->make('InvoicesService')->list($past, $future, '0'); - $this->assertEquals(0, $invoices->count()); + app()->make('InvoicesService')->saveMovements($invoice->id, [ + 'type' => ['invoice-payment'], + 'amount' => [10], + 'method' => ['bank'], + 'notes' => [''], + ]); - $this->nextRound(); + $this->nextRound(); + + $movements = Movement::where('type', 'invoice-payment')->get(); + $this->assertEquals(1, $movements->count()); + $movement = $movements->first(); + $this->assertEquals(10, $movement->amount); $invoice = app()->make('InvoicesService')->show($invoice->id); + $this->assertEquals($invoice->payment->id, $movement->id); + $this->assertEquals($invoice->status, 'payed'); + foreach($invoice->orders as $order) { $this->assertEquals($movement->id, $order->payment_id); } } + /* + Pagamento fattura con permessi sbagliati + */ + public function testWrongPayment() + { + $this->expectException(AuthException::class); + + $invoice = $this->createInvoice(); + $this->nextRound(); + $this->wireOrders($invoice); + $this->nextRound(); + + $this->actingAs($this->userWithInvoicesPerm); + + app()->make('InvoicesService')->saveMovements($invoice->id, [ + 'type' => ['invoice-payment'], + 'amount' => [10], + 'method' => ['bank'], + 'notes' => [''], + ]); + } + /* Modifica Fattura con permessi sbagliati */ @@ -213,7 +285,7 @@ public function testFailsToUpdate() public function testFailsToUpdateBecauseNoUserWithID() { $this->expectException(ModelNotFoundException::class); - $this->actingAs($this->userWithAdminPerm); + $this->actingAs($this->userWithSupplierPerm); app()->make('InvoicesService')->update('id', array()); } @@ -250,9 +322,12 @@ public function testFailsToShowInexistent() */ public function testFailsToDestroy() { + $invoice = $this->createInvoice(); + $this->nextRound(); + $this->expectException(AuthException::class); $this->actingAs($this->userWithNoPerms); - app()->make('InvoicesService')->destroy('random'); + app()->make('InvoicesService')->destroy($invoice->id); } /* @@ -270,7 +345,7 @@ public function testDestroy() $this->nextRound(); - $movement = \App\Movement::generate('invoice-payment', $this->userWithAdminPerm->gas, $invoice, 10); + $movement = Movement::generate('invoice-payment', $this->userWithSupplierPerm->gas, $invoice, 10); $movement->save(); $this->nextRound(); diff --git a/code/tests/Services/RolesServiceTest.php b/code/tests/Services/RolesServiceTest.php index 3424c51e..39caa372 100644 --- a/code/tests/Services/RolesServiceTest.php +++ b/code/tests/Services/RolesServiceTest.php @@ -81,13 +81,15 @@ public function testStore() $this->nextRound(); app()->make('RolesService')->attachUser($this->userWithNoPerms->id, $role->id, null); - $target = $this->userWithNoPerms->targetsByAction('supplier.view'); + $user = app()->make('UsersService')->show($this->userWithNoPerms->id); + $target = $user->targetsByAction('supplier.view'); $this->assertTrue(count($target) > 0); $this->nextRound(); app()->make('RolesService')->detachUser($this->userWithNoPerms->id, $role->id, null); - $target = $this->userWithNoPerms->targetsByAction('supplier.view'); + $user = app()->make('UsersService')->show($this->userWithNoPerms->id); + $target = $user->targetsByAction('supplier.view'); $this->assertTrue(count($target) == 0); }