PluginProbe
ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce / 1.2.6
ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce v1.2.6
1.17.9 1.17.8 1.17.7 1.17.6 1.17.5 1.17.4 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.3.0 1.3.1 1.3.11 1.3.12 1.3.13 1.3.14 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 All 143 releases
erp / includes / api / class-sales-controller.php

class-sales-controller.php in ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce 1.2.6, at includes/api/class-sales-controller.php

592 lines 22.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WeDevs\ERP\API;
3
4 use WP_REST_Server;
5 use WP_REST_Response;
6 use WP_Error;
7
8 class Sales_Controller extends REST_Controller {
9 /**
10 * Endpoint namespace.
11 *
12 * @var string
13 */
14 protected $namespace = 'erp/v1';
15
16 /**
17 * Route base.
18 *
19 * @var string
20 */
21 protected $rest_base = 'accounting/sales';
22
23 /**
24 * Register the routes for the objects of the controller.
25 */
26 public function register_routes() {
27 register_rest_route( $this->namespace, '/' . $this->rest_base, [
28 [
29 'methods' => WP_REST_Server::READABLE,
30 'callback' => [ $this, 'get_sales' ],
31 'args' => $this->get_collection_params(),
32 'permission_callback' => function ( $request ) {
33 return current_user_can( 'erp_ac_view_sale' );
34 },
35 ],
36 [
37 'methods' => WP_REST_Server::CREATABLE,
38 'callback' => [ $this, 'create_sale' ],
39 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
40 'permission_callback' => [ $this, 'create_update_permission_check' ],
41 ],
42 'schema' => [ $this, 'get_public_item_schema' ],
43 ] );
44
45 register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', [
46 [
47 'methods' => WP_REST_Server::READABLE,
48 'callback' => [ $this, 'get_sale' ],
49 'args' => [
50 'context' => $this->get_context_param( [ 'default' => 'view' ] ),
51 ],
52 'permission_callback' => function ( $request ) {
53 return current_user_can( 'erp_ac_view_sale' );
54 },
55 ],
56 [
57 'methods' => WP_REST_Server::EDITABLE,
58 'callback' => [ $this, 'update_sale' ],
59 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
60 'permission_callback' => [ $this, 'create_update_permission_check' ],
61 ],
62 [
63 'methods' => WP_REST_Server::DELETABLE,
64 'callback' => [ $this, 'delete_sale' ],
65 'permission_callback' => function ( $request ) {
66 return current_user_can( 'erp_ac_view_sale' );
67 },
68 ],
69 'schema' => [ $this, 'get_public_item_schema' ],
70 ] );
71 }
72
73 /**
74 * Get a collection of sales
75 *
76 * @param WP_REST_Request $request
77 *
78 * @return WP_Error|WP_REST_Response
79 */
80 public function get_sales( $request ) {
81 $args = [
82 'number' => $request['per_page'],
83 'offset' => ( $request['per_page'] * ( $request['page'] - 1 ) ),
84 'type' => 'sales',
85 'join' => ['items'],
86 ];
87
88 $items = erp_ac_get_all_transaction( $args );
89 $total_items = erp_ac_get_transaction_count( ['type' => 'sales'] );
90
91 $formated_items = [];
92 foreach ( $items as $item ) {
93 $additional_fields = [];
94
95 $data = $this->prepare_item_for_response( $item, $request, $additional_fields );
96 $formated_items[] = $this->prepare_response_for_collection( $data );
97 }
98
99 $response = rest_ensure_response( $formated_items );
100 $response = $this->format_collection_response( $response, $request, $total_items );
101
102 return $response;
103 }
104
105 /**
106 * Get a specific sale
107 *
108 * @param WP_REST_Request $request
109 *
110 * @return WP_Error|WP_REST_Response
111 */
112 public function get_sale( $request ) {
113 $id = (int) $request['id'];
114 $item = \WeDevs\ERP\Accounting\Model\Transaction::find( $id );
115
116 if ( empty( $id ) || empty( $item->id ) || $item->type != 'sales' ) {
117 return new WP_Error( 'rest_sale_invalid_id', __( 'Invalid resource id.' ), [ 'status' => 404 ] );
118 }
119
120 $item->items = $item->items->toArray();
121
122 $additional_fields = [];
123
124 $item = $this->prepare_item_for_response( $item, $request, $additional_fields );
125 $response = rest_ensure_response( $item );
126
127 return $response;
128 }
129
130 /**
131 * Create a sale
132 *
133 * @param WP_REST_Request $request
134 *
135 * @return WP_Error|WP_REST_Request
136 */
137 public function create_sale( $request ) {
138 $trans_data = $this->prepare_item_for_database( $request );
139
140 if ( empty( $request['form_type'] ) || ! in_array( $request['form_type'], ['payment', 'invoice'] ) ) {
141 return new WP_Error( 'rest_sale_invalid_form_type', __( 'Invalid form type.' ), [ 'status' => 400 ] );
142 }
143
144 if ( empty( $request['customer'] ) ) {
145 return new WP_Error( 'rest_sale_invalid_customer', __( 'Required customer.' ), [ 'status' => 400 ] );
146 }
147
148 if ( $request['form_type'] == 'invoice' && ( ! isset( $request['due_date'] ) || empty( $request['due_date'] ) ) ) {
149 return new WP_Error( 'rest_sale_invalid_due_date', __( 'Required due_date field.' ), [ 'status' => 400 ] );
150 }
151
152 if ( $request['form_type'] == 'payment' && ( ! isset( $request['account_id'] ) || empty( $request['account_id'] ) ) ) {
153 return new WP_Error( 'rest_sale_invalid_account_id', __( 'Required account_id field.' ), [ 'status' => 400 ] );
154 }
155
156 if ( $trans_data['form_type'] == 'payment' ) {
157 $due_transactions = erp_ac_get_all_transaction([
158 'status' => ['in' => ['awaiting_payment', 'partial']],
159 'user_id' => $request['customer'],
160 'parent' => 0,
161 'type' => 'sales',
162 'join' => ['journals'],
163 'with_ledger' => true,
164 'output_by' => 'array',
165 ]);
166
167 if ( ! empty( $due_transactions ) && empty( $request['partial_id'] ) ) {
168 $due_items = [];
169
170 foreach ( $due_transactions as $due_transaction ) {
171 $due_items[] = [
172 'transaction_id' => (int) $due_transaction['id'],
173 'account_id' => 1,
174 'due' => (float) $due_transaction['due'],
175 ];
176 }
177
178 $error_response = rest_ensure_response( [
179 'code' => 'rest_sale_due_invoices',
180 'message' => __( 'You\'ve some due invoices.', 'erp' ),
181 'data' => $due_items
182 ] );
183 $error_response->set_status( 404 );
184
185 return $error_response;
186 }
187
188 if ( ! empty( $request['partial_id'] ) ) {
189 $trans_data['status'] = 'closed';
190 }
191 }
192
193 $items = $this->prepare_trans_items_for_database( $request );
194
195 $tax_total = array_reduce( $items, function( $total, $value ) {
196 return $total + $value['tax_rate'];
197 } );
198
199 $trans_data['sub_total'] = array_reduce( $items, function( $total, $value ) {
200 return $total + $value['line_total'];
201 } );
202
203 $trans_data['trans_total'] = $trans_data['sub_total'] + $tax_total;
204 $trans_data['total'] = $trans_data['trans_total'];
205 $trans_data['line_total'] = array_pluck( $items, 'line_total' );
206
207 if ( $trans_data['form_type'] == 'invoice' ) {
208 $trans_data['due'] = $trans_data['total'];
209 }
210
211 $id = erp_ac_insert_transaction( $trans_data, $items );
212
213 if ( is_wp_error( $id ) ) {
214 return $id;
215 }
216
217 $transaction = \WeDevs\ERP\Accounting\Model\Transaction::find( $id );
218 $transaction->items = $transaction->items->toArray();
219
220 $request->set_param( 'context', 'edit' );
221 $response = $this->prepare_item_for_response( $transaction, $request );
222 $response = rest_ensure_response( $response );
223 $response->set_status( 201 );
224 $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) );
225
226 return $response;
227 }
228
229 /**
230 * Update a sale
231 *
232 * @param WP_REST_Request $request
233 *
234 * @return WP_Error|WP_REST_Request
235 */
236 public function update_sale( $request ) {
237 $id = (int) $request['id'];
238
239 $item = (object) erp_ac_get_transaction( $id );
240 if ( ! $item ) {
241 return new WP_Error( 'rest_sale_invalid_id', __( 'Invalid resource id.' ), [ 'status' => 400 ] );
242 }
243
244 if ( empty( $request['form_type'] ) || ! in_array( $request['form_type'], ['payment', 'invoice'] ) ) {
245 return new WP_Error( 'rest_sale_invalid_form_type', __( 'Invalid form type.' ), [ 'status' => 400 ] );
246 }
247
248 if ( empty( $request['customer'] ) ) {
249 return new WP_Error( 'rest_sale_invalid_customer', __( 'Required customer.' ), [ 'status' => 400 ] );
250 }
251
252 if ( $request['form_type'] == 'invoice' && ( ! isset( $request['due_date'] ) || empty( $request['due_date'] ) ) ) {
253 return new WP_Error( 'rest_sale_invalid_due_date', __( 'Required due_date field.' ), [ 'status' => 400 ] );
254 }
255
256 if ( $request['form_type'] == 'payment' && ( ! isset( $request['account_id'] ) || empty( $request['account_id'] ) ) ) {
257 return new WP_Error( 'rest_sale_invalid_account_id', __( 'Required account_id field.' ), [ 'status' => 400 ] );
258 }
259
260 $trans_data = $this->prepare_item_for_database( $request );
261
262 $items = $this->prepare_trans_items_for_database( $request );
263
264 $tax_total = array_reduce( $items, function( $total, $value ) {
265 return $total + $value['tax_rate'];
266 } );
267
268 $trans_data['sub_total'] = array_reduce( $items, function( $total, $value ) {
269 return $total + $value['line_total'];
270 } );
271
272 $trans_data['trans_total'] = $trans_data['sub_total'] + $tax_total;
273 $trans_data['total'] = $trans_data['trans_total'];
274 $trans_data['line_total'] = array_pluck( $items, 'line_total' );
275
276 $id = erp_ac_insert_transaction( $trans_data, $items );
277
278 if ( is_wp_error( $id ) ) {
279 return $id;
280 }
281
282 $transaction = \WeDevs\ERP\Accounting\Model\Transaction::find( $id );
283 $transaction->items = $transaction->items->toArray();
284
285 $response = $this->prepare_item_for_response( $transaction, $request );
286 $response = rest_ensure_response( $response );
287 $response->set_status( 201 );
288 $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) );
289
290 return $response;
291 }
292
293 /**
294 * Delete a sale
295 *
296 * @param WP_REST_Request $request
297 *
298 * @return WP_Error|WP_REST_Request
299 */
300 public function delete_sale( $request ) {
301 $id = (int) $request['id'];
302
303 $result = erp_ac_remove_transaction( $id );
304
305 if ( is_wp_error( $result ) ) {
306 return $result;
307 }
308
309 return new WP_REST_Response( true, 204 );
310 }
311
312 /**
313 * Prepare a single item for create or update
314 *
315 * @param WP_REST_Request $request Request object.
316 *
317 * @return array $prepared_item
318 */
319 protected function prepare_item_for_database( $request ) {
320 $prepared_item = [];
321
322 $prepared_item['id'] = isset( $request['id'] ) ? intval( $request['id'] ) : 0;
323 $prepared_item['type'] = 'sales';
324 $prepared_item['form_type'] = isset( $request['form_type'] ) ? sanitize_text_field( $request['form_type'] ) : '';
325 $prepared_item['account_id'] = isset( $request['account_id'] ) ? intval( $request['account_id'] ) : 0;
326
327 if ( $request['form_type'] == 'payment' ) {
328 $prepared_item['partial_id'] = ! empty( $request['partial_id'] ) ? explode( ",", str_replace( " ", "", $request['partial_id'] ) ) : [];
329 }
330
331 if ( $request['form_type'] == 'invoice' ) {
332 $prepared_item['account_id'] = 1;
333 }
334
335 $prepared_item['status'] = ( $prepared_item['form_type'] == 'payment' ) ? 'closed' : 'draft';
336 $prepared_item['user_id'] = isset( $request['customer'] ) ? intval( $request['customer'] ) : 0;
337 $prepared_item['billing_address'] = isset( $request['billing_address'] ) ? wp_kses_post( $request['billing_address'] ) : '';
338 $prepared_item['ref'] = isset( $request['reference'] ) ? sanitize_text_field( $request['reference'] ) : '';
339 $prepared_item['issue_date'] = isset( $request['issue_date'] ) ? sanitize_text_field( $request['issue_date'] ) : '';
340 $prepared_item['due_date'] = isset( $request['due_date'] ) ? sanitize_text_field( $request['due_date'] ) : '';
341 $prepared_item['summary'] = isset( $request['summary'] ) ? wp_kses_post( $request['summary'] ) : '';
342 $prepared_item['currency'] = isset( $request['currency'] ) ? sanitize_text_field( $request['currency'] ) : 'USD';
343
344 return $prepared_item;
345 }
346
347 /**
348 * Prepare a single user output for response
349 *
350 * @param object $item
351 * @param WP_REST_Request $request Request object.
352 * @param array $additional_fields (optional)
353 *
354 * @return WP_REST_Response $response Response data.
355 */
356 public function prepare_item_for_response( $item, $request, $additional_fields = [] ) {
357 $data = [
358 'id' => (int) $item->id,
359 'form_type' => $item->form_type,
360 'status' => $item->status,
361 'billing_address' => $item->billing_address,
362 'reference' => $item->ref,
363 'summary' => $item->summary,
364 'issue_date' => $item->issue_date,
365 'due_date' => $item->due_date,
366 'currency' => $item->currency,
367 'conversion_rate' => (float) $item->conversion_rate,
368 'items' => $this->format_transaction_items( $item->items ),
369 'sub_total' => (float) $item->sub_total,
370 'total' => (float) $item->total,
371 'due' => (float) $item->due,
372 'trans_total' => (float) $item->trans_total,
373 'invoice' => erp_ac_get_invoice_number( $item->invoice_number, $item->invoice_format ),
374 'parent' => (int) $item->parent,
375 'created_at' => $item->created_at,
376 ];
377
378 if ( isset( $request['include'] ) ) {
379 $include_params = explode( ',', str_replace( ' ', '', $request['include'] ) );
380
381 if ( in_array( 'customer', $include_params ) ) {
382 $customers_controller = new Customers_Controller();
383
384 $customer_id = (int) $item->user_id;
385 $data['customer'] = null;
386
387 if ( $customer_id ) {
388 $customer = $customers_controller->get_customer( ['id' => $customer_id ] );
389 $data['customer'] = ! is_wp_error( $customer ) ? $customer->get_data() : null;
390 }
391 }
392
393 if ( in_array( 'created_by', $include_params ) ) {
394 $data['created_by'] = $this->get_user( intval( $item->created_by ) );
395 }
396 }
397
398 $data = array_merge( $data, $additional_fields );
399
400 // Wrap the data in a response object
401 $response = rest_ensure_response( $data );
402
403 $response = $this->add_links( $response, $item );
404
405 return $response;
406 }
407
408 /**
409 * Prepare transaction items for database
410 *
411 * @param array $request
412 *
413 * @return array
414 */
415 protected function prepare_trans_items_for_database( $request ) {
416 $taxes = erp_ac_get_tax_info();
417 $taxes = array_pluck( $taxes, 'rate', 'id' );
418
419 foreach ( $request['items'] as $item ) {
420 $unit_price = (float) erp_ac_format_decimal( $item['unit_price'] );
421 $qty = intval( $item['qty'] );
422 $discount = (int) erp_ac_format_decimal( $item['discount'] );
423 $tax = isset( $item['tax'] ) ? $item['tax'] : 0;
424
425 $items[] = [
426 'item_id' => isset( $item['id'] ) ? intval( $item['id'] ) : 0,
427 'journal_id' => isset( $item['journal_id'] ) ? intval( $item['journal_id'] ) : 0,
428 'product_id' => isset( $item['product_id'] ) ? intval( $item['product_id'] ) : 0,
429 'account_id' => isset( $item['account_id'] ) ? intval( $item['account_id'] ) : 0,
430 'description' => sanitize_text_field( $item['description'] ),
431 'qty' => $qty,
432 'unit_price' => $unit_price,
433 'discount' => $discount,
434 'line_total' => ( ( $unit_price * $qty ) - $discount ),
435 'tax' => $tax,
436 'tax_rate' => isset( $taxes[ $tax ] ) ? $taxes[ $tax ] : 0,
437 'tax_journal' => isset( $item['tax_journal'] ) ? $item['tax_journal'] : 0
438 ];
439 }
440
441 return $items;
442 }
443
444 /**
445 * Format the transaction's items
446 *
447 * @param array $items
448 *
449 * @return array
450 */
451 protected function format_transaction_items( $items ) {
452 return array_map( function( $item ) {
453 return [
454 'id' => (int) $item['id'],
455 'journal_id' => (int) $item['journal_id'],
456 'product_id' => (int) $item['product_id'],
457 'description' => $item['description'],
458 'qty' => (int) $item['qty'],
459 'unit_price' => (float) $item['unit_price'],
460 'discount' => (float) $item['discount'],
461 'tax' => (float) $item['tax'],
462 'tax_rate' => (float) $item['tax_rate'],
463 'tax_journal' => (int) $item['tax_journal'],
464 'line_total' => (float) $item['line_total'],
465 'order' => (int) $item['order'],
466 ];
467 }, $items );
468 }
469
470 /**
471 * Get the User's schema, conforming to JSON Schema
472 *
473 * @return array
474 */
475 public function get_item_schema() {
476 $schema = [
477 '$schema' => 'http://json-schema.org/draft-04/schema#',
478 'title' => 'sale',
479 'type' => 'object',
480 'properties' => [
481 'id' => [
482 'description' => __( 'Unique identifier for the resource.' ),
483 'type' => 'integer',
484 'context' => [ 'embed', 'view', 'edit' ],
485 'readonly' => true,
486 ],
487 'form_type' => [
488 'description' => __( 'Form type for the resource.' ),
489 'type' => 'string',
490 'context' => [ 'edit' ],
491 'arg_options' => [
492 'sanitize_callback' => 'sanitize_text_field',
493 ],
494 'required' => true,
495 ],
496 'account_id' => [
497 'description' => __( 'Account id for the resource.' ),
498 'type' => 'integer',
499 'context' => [ 'edit' ],
500 ],
501 'billing_address' => [
502 'description' => __( 'Billing address for the resource.' ),
503 'type' => 'string',
504 'context' => [ 'edit' ],
505 'arg_options' => [
506 'sanitize_callback' => 'sanitize_text_field',
507 ],
508 ],
509 'reference' => [
510 'description' => __( 'Reference for the resource.' ),
511 'type' => 'string',
512 'context' => [ 'edit' ],
513 'arg_options' => [
514 'sanitize_callback' => 'sanitize_text_field',
515 ],
516 ],
517 'summary' => [
518 'description' => __( 'Summary for the resource.' ),
519 'type' => 'string',
520 'context' => [ 'edit' ],
521 'arg_options' => [
522 'sanitize_callback' => 'sanitize_text_field',
523 ],
524 ],
525 'issue_date' => [
526 'description' => __( 'Issue date for the resource.' ),
527 'type' => 'string',
528 'context' => [ 'edit' ],
529 'arg_options' => [
530 'sanitize_callback' => 'sanitize_text_field',
531 ],
532 'required' => true,
533 ],
534 'due_date' => [
535 'description' => __( 'Due date for the resource.' ),
536 'type' => 'string',
537 'context' => [ 'edit' ],
538 'arg_options' => [
539 'sanitize_callback' => 'sanitize_text_field',
540 ],
541 ],
542 'currency' => [
543 'description' => __( 'Currency for the resource.' ),
544 'type' => 'string',
545 'context' => [ 'edit' ],
546 'arg_options' => [
547 'sanitize_callback' => 'sanitize_text_field',
548 ],
549 ],
550 'customer' => [
551 'description' => __( 'Customer for the resource.' ),
552 'type' => 'integer',
553 'context' => [ 'edit' ],
554 'required' => true,
555 ],
556 'items' => [
557 'description' => __( 'Items for the resource.' ),
558 'type' => 'array',
559 'context' => [ 'edit' ],
560 'required' => true,
561 ],
562 ],
563 ];
564
565 return $schema;
566 }
567
568 /**
569 * Sale create/update permission check
570 *
571 * @param [type] $request
572 *
573 * @return boolean
574 */
575 public function create_update_permission_check( $request ) {
576 $form_type = isset( $request['form_type'] ) ? $request['form_type'] : '';
577
578 switch ( $form_type ) {
579 case 'invoice':
580 return current_user_can( 'erp_ac_create_sales_invoice' );
581 break;
582
583 case 'payment':
584 return current_user_can( 'erp_ac_create_sales_payment' );
585 break;
586
587 default:
588 return false;
589 break;
590 }
591 }
592 }