-
Notifications
You must be signed in to change notification settings - Fork 0
/
PurchaseOrderController.php
221 lines (195 loc) · 8.68 KB
/
PurchaseOrderController.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\StorePurchaseOrderRequest;
use App\Http\Requests\Api\UpdatePurchaseOrderRequest;
use App\Http\Resources\Api\PurchaseOrderResource;
use App\Models\Item;
use App\Models\PurchaseOrder;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Facades\DB;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class PurchaseOrderController extends Controller
{
/**
* Get all purchase paginated in descending order with search query param
* @return AnonymousResourceCollection
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function index(): AnonymousResourceCollection
{
$purchaseOrders = PurchaseOrder::query();
$search = request()->get('search');
if($search) {
$purchaseOrders = $purchaseOrders->where('po_number','like','%'.$search.'%')->
orWhere('buyer_name','like','%'.$search.'%');
}
$purchaseOrders = $purchaseOrders->orderBy('id', 'desc')->paginate(10);
return PurchaseOrderResource::collection($purchaseOrders);
}
/**
* Get single purchase order with the related items
* @param PurchaseOrder $purchaseOrder
* @return PurchaseOrderResource
*/
public function show(PurchaseOrder $purchaseOrder): PurchaseOrderResource
{
return new PurchaseOrderResource($purchaseOrder->load('items'));
}
/**
* Create new purchase order with items
* @param StorePurchaseOrderRequest $request
* @return JsonResponse
*/
public function store(StorePurchaseOrderRequest $request): JsonResponse
{
// Use DB transactions to ensure that items are inserted with the order
DB::transaction(function () use ($request) {
// Calculate order total before inserting the order itself to avoid one more query
$total = collect($request->items)->sum(function ($item) {
return $item['unit_price'] * $item['quantity'];
});
// Create purchase order
$order = PurchaseOrder::create($request->except(['items']) + ['total' => $total]);
// Create purchase order items
$order->items()->createMany($request->only(['items'])['items']);
});
return response()->json(['message' => 'Purchase order created successfully!']);
}
/**
* Update the given purchase order in the following way
* - All purchase order information are optional (if field doesn't exist in the request, then it will stay as is)
* - If the item has an id, item information will be updated
* - If the item doesn't have ID, it will be considered as a new item
* - Delete the items that not exist in request
* - Making sure that the Total value in the purchase order is updated accordingly
* @smell This method is created to showcase another option for updating the PO
* @param UpdatePurchaseOrderRequest $request
* @param PurchaseOrder $purchaseOrder
* @return JsonResponse
*/
public function old_update(
UpdatePurchaseOrderRequest $request,
PurchaseOrder $purchaseOrder
): JsonResponse {
// Wrap all DB queries in a transaction
DB::transaction(function () use ($request, $purchaseOrder) {
// Updated or create items
if ($request->items) {
$orderItemIds = $purchaseOrder->items->pluck('id')->toArray();
foreach ($request->items as $item) {
// Updated the Item if given item request has id, and it is one of the order items
if (isset($item['id']) && in_array($item['id'], $orderItemIds)) {
$purchaseOrder->items()->where('id', $item['id'])->update($item);
} else {
// Create new item if the id is not given or was not related to the given Purchase order
$purchaseOrder->items()->create($item);
}
}
}
// Calculate and update the total of the purchase order based on updated items
$total = $purchaseOrder->items->sum(function ($item) {
return $item->quantity * $item->unit_price;
});
$purchaseOrder->update($request->except(['items']) + ['total' => $total]);
});
return response()->json(['message' => 'Purchase order updated successfully!']);
}
/**
* Update the given purchase order in the following way
* - All purchase order information should exist
* - If the item has an id, item information will be updated
* - If the item doesn't have an ID, it will be considered as a new item
* - Delete the items that is not exist in request (except for the new items)
* - Making sure that the Total value in the purchase order is updated accordingly
* @param UpdatePurchaseOrderRequest $request
* @param PurchaseOrder $purchaseOrder
* @return JsonResponse
*/
public function update(
UpdatePurchaseOrderRequest $request,
PurchaseOrder $purchaseOrder
): JsonResponse {
// Wrap all DB queries in a transaction
DB::transaction(function () use ($request, $purchaseOrder) {
// Updated or create items
// Get all items ids for this PO as array
$orderItemIds = $purchaseOrder->items->pluck('id')->toArray();
$requestItemIds = [];
// Iterate over the request items
foreach ($request->items as $item) {
// Update the Item if given item request has an id, and it is one of the order items
if (isset($item['id']) && in_array($item['id'], $orderItemIds)) {
$requestItemIds[] = $item['id'];
$requestItemData = collect($item)->only(['description','quantity','unit_price', 'category_id'])->toArray();
$purchaseOrder->items()->where('id', $item['id'])->update($requestItemData);
} else {
// Create new item if the id is not given or was not related to the given Purchase order
$newItem = $purchaseOrder->items()->create($item);
$requestItemIds[] = $newItem->id;
}
}
// Delete items that not exist in the request
$purchaseOrder->items()->whereNotIn('id', $requestItemIds)->delete();
// Calculate the total of the purchase order after adding / updating / deleting the items
$total = $purchaseOrder->items()->sum(DB::raw('quantity * unit_price'));
// Update the purchase order information
$purchaseOrder->update($request->except(['items','total']) + ['total' => $total]);
});
return response()->json(['message' => 'Purchase order updated successfully!']);
}
/**
* Delete single purchase order
* @param PurchaseOrder $purchaseOrder
* @return JsonResponse
*/
public function destroy(PurchaseOrder $purchaseOrder): JsonResponse
{
try{
$purchaseOrder->delete();
}catch (\Exception $e){
return response()->json(['message' => $e->getMessage()], 500);
}
return response()->json(['message' => 'Purchase order deleted successfully!']);
}
/**
* Delete multiple purchase orders
* @param Request $request
* @return JsonResponse
*/
public function destroyMany(Request $request): JsonResponse
{
DB::beginTransaction();
try {
$deleted = PurchaseOrder::destroy($request->ids);
if($deleted !== count($request->ids)){
DB::rollBack();
return response()->json(['message' => 'One of the given ids is not found'], 404);
}
} catch(\Exception $e) {
DB::rollBack();
return response()->json(['message' => $e->getMessage()], 500);
}
DB::commit();
return response()->json(['message' => 'Purchase orders deleted successfully!']);
}
/**
* @smell not covered in unit tests
* @return mixed
*/
public function groupedByDay(): mixed
{
$startDate = Carbon::now()->startOfMonth();
$endDate = Carbon::now()->endOfMonth();
return PurchaseOrder::selectRaw("DATE_FORMAT(created_at, '%b %d') as date, COUNT(*) as count")
->whereBetween('created_at', [$startDate, $endDate])
->groupBy('date')
->orderBy('date', 'asc')
->get();
}
}