PluginProbe
ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce / 1.6.7
ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce v1.6.7
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 / modules / accounting / includes / functions / invoices.php

invoices.php in ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce 1.6.7, at modules/accounting/includes/functions/invoices.php

1,064 lines 34.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit; // Exit if accessed directly
5 }
6
7 /**
8 * Get all invoices
9 *
10 * @return mixed
11 */
12 function erp_acct_get_all_invoices( $args = [] ) {
13 global $wpdb;
14
15 $defaults = [
16 'number' => 20,
17 'offset' => 0,
18 'orderby' => 'id',
19 'order' => 'DESC',
20 'count' => false,
21 's' => '',
22 ];
23
24 $args = wp_parse_args( $args, $defaults );
25
26 $where = '';
27 $limit = '';
28
29 if ( ! empty( $args['start_date'] ) ) {
30 $where .= "WHERE invoice.trn_date BETWEEN '{$args['start_date']}' AND '{$args['end_date']}'";
31 }
32
33 if ( '-1' === $args['number'] ) {
34 $limit = "LIMIT {$args['number']} OFFSET {$args['offset']}";
35 }
36
37 $sql = 'SELECT';
38
39 if ( $args['count'] ) {
40 $sql .= ' COUNT( DISTINCT invoice.id ) as total_number';
41 } else {
42 $sql .= ' invoice.*, SUM(ledger_detail.credit) - SUM(ledger_detail.debit) as due';
43 }
44
45 $sql .= " FROM {$wpdb->prefix}erp_acct_invoices AS invoice LEFT JOIN {$wpdb->prefix}erp_acct_ledger_details AS ledger_detail";
46 $sql .= " ON invoice.voucher_no = ledger_detail.trn_no {$where} GROUP BY invoice.voucher_no ORDER BY invoice.{$args['orderby']} {$args['order']} {$limit}";
47
48 if ( $args['count'] ) {
49 return $wpdb->get_var( $sql );
50 }
51
52 return $wpdb->get_results( $sql, ARRAY_A );
53 }
54
55 /**
56 * Get an single invoice
57 *
58 * @param $invoice_no
59 *
60 * @return mixed
61 */
62 function erp_acct_get_invoice( $invoice_no ) {
63 global $wpdb;
64
65 $sql = $wpdb->prepare(
66 "Select
67
68 voucher.editable,
69 voucher.currency,
70
71 invoice.id,
72 invoice.voucher_no,
73 invoice.customer_id,
74 invoice.customer_name,
75 invoice.trn_date,
76 invoice.due_date,
77 invoice.billing_address,
78 invoice.amount,
79 invoice.discount,
80 invoice.discount_type,
81 invoice.tax,
82 invoice.estimate,
83 invoice.attachments,
84 invoice.status,
85 invoice.particulars,
86 invoice.created_at,
87
88 inv_acc_detail.debit,
89 inv_acc_detail.credit
90
91 FROM {$wpdb->prefix}erp_acct_invoices as invoice
92 LEFT JOIN {$wpdb->prefix}erp_acct_voucher_no as voucher ON invoice.voucher_no = voucher.id
93 LEFT JOIN {$wpdb->prefix}erp_acct_invoice_account_details as inv_acc_detail ON invoice.voucher_no = inv_acc_detail.trn_no
94 WHERE invoice.voucher_no = %d",
95 $invoice_no
96 );
97
98 $row = $wpdb->get_row( $sql, ARRAY_A );
99
100 $row['line_items'] = erp_acct_format_invoice_line_items( $invoice_no );
101 $row['tax_rate_id'] = erp_acct_get_default_tax_rate_name_id();
102
103 // calculate every line total
104 foreach ( $row['line_items'] as $key => $value ) {
105 $total = ( $value['item_total'] + $value['tax'] ) - $value['discount'];
106 $row['line_items'][ $key ]['line_total'] = $total;
107 }
108
109 $row['attachments'] = unserialize( $row['attachments'] );
110 $row['total_due'] = erp_acct_get_invoice_due( $invoice_no );
111 $row['pdf_link'] = erp_acct_pdf_abs_path_to_url( $invoice_no );
112
113 return $row;
114 }
115
116 /**
117 * Get formatted line items
118 */
119 function erp_acct_format_invoice_line_items( $voucher_no ) {
120 global $wpdb;
121
122 $sql = $wpdb->prepare(
123 "SELECT
124 inv_detail.product_id,
125 inv_detail.qty,
126 inv_detail.unit_price,
127 inv_detail.discount,
128 inv_detail.tax,
129 inv_detail.item_total,
130 inv_detail.ecommerce_type,
131
132 SUM(inv_detail_tax.tax_rate) as tax_rate,
133
134 product.name,
135 product.product_type_id,
136 product.category_id,
137 product.vendor,
138 product.cost_price,
139 product.sale_price,
140 product.tax_cat_id
141
142 FROM {$wpdb->prefix}erp_acct_invoices as invoice
143 LEFT JOIN {$wpdb->prefix}erp_acct_invoice_details as inv_detail ON invoice.voucher_no = inv_detail.trn_no
144 LEFT JOIN {$wpdb->prefix}erp_acct_invoice_details_tax as inv_detail_tax ON inv_detail.id = inv_detail_tax.invoice_details_id
145 LEFT JOIN {$wpdb->prefix}erp_acct_products as product ON inv_detail.product_id = product.id
146 WHERE invoice.voucher_no = %d GROUP BY inv_detail.id",
147 $voucher_no
148 );
149
150 $results = $wpdb->get_results( $sql, ARRAY_A );
151
152 if ( ! empty( reset( $results )['ecommerce_type'] ) ) {
153 // product name should not fetch form `erp_acct_products`
154 $results = array_map(
155 function ( $result ) {
156 $result['name'] = get_the_title( $result['product_id'] );
157
158 return $result;
159 },
160 $results
161 );
162 }
163
164 return $results;
165 }
166
167 /**
168 * Insert invoice data
169 *
170 * @param $data
171 *
172 * @return int
173 */
174 function erp_acct_insert_invoice( $data ) {
175 global $wpdb;
176
177 $user_id = get_current_user_id();
178
179 $data['created_at'] = date( 'Y-m-d H:i:s' );
180 $data['created_by'] = $user_id;
181 $data['updated_at'] = date( 'Y-m-d H:i:s' );
182 $data['updated_by'] = $user_id;
183
184 $voucher_no = null;
185 $estimate_type = 1;
186 $draft = 1;
187 $currency = erp_get_currency( true );
188 $email = erp_get_people_email( $data['customer_id'] );
189
190 try {
191 $wpdb->query( 'START TRANSACTION' );
192
193 $wpdb->insert(
194 $wpdb->prefix . 'erp_acct_voucher_no',
195 [
196 'type' => 'invoice',
197 'currency' => $currency,
198 'editable' => 1,
199 'created_at' => $data['created_at'],
200 'created_by' => $data['created_by'],
201 ]
202 );
203
204 $voucher_no = $wpdb->insert_id;
205
206 $invoice_data = erp_acct_get_formatted_invoice_data( $data, $voucher_no );
207
208 $wpdb->insert(
209 $wpdb->prefix . 'erp_acct_invoices',
210 [
211 'voucher_no' => $invoice_data['voucher_no'],
212 'customer_id' => $invoice_data['customer_id'],
213 'customer_name' => $invoice_data['customer_name'],
214 'trn_date' => $invoice_data['trn_date'],
215 'due_date' => $invoice_data['due_date'],
216 'billing_address' => $invoice_data['billing_address'],
217 'amount' => $invoice_data['amount'],
218 'discount' => $invoice_data['discount'],
219 'discount_type' => $invoice_data['discount_type'],
220 'tax' => $invoice_data['tax'],
221 'estimate' => $invoice_data['estimate'],
222 'attachments' => $invoice_data['attachments'],
223 'status' => $invoice_data['status'],
224 'particulars' => $invoice_data['particulars'],
225 'created_at' => $invoice_data['created_at'],
226 'created_by' => $invoice_data['created_by'],
227 ]
228 );
229
230 erp_acct_insert_invoice_details_and_tax( $invoice_data, $voucher_no );
231
232 if ( $estimate_type === $invoice_data['estimate'] || $draft === $invoice_data['status'] ) {
233 $wpdb->query( 'COMMIT' );
234 $estimate = erp_acct_get_invoice( $voucher_no );
235 $estimate['email'] = $email;
236 do_action( 'erp_acct_new_transaction_estimate', $voucher_no, $estimate );
237
238 return $estimate;
239 }
240
241 erp_acct_insert_invoice_account_details( $invoice_data, $voucher_no );
242 erp_acct_insert_invoice_data_into_ledger( $invoice_data );
243
244 do_action( 'erp_acct_after_sales_create', $data, $voucher_no );
245
246 $data['dr'] = $invoice_data['amount'];
247 $data['cr'] = 0;
248 erp_acct_insert_data_into_people_trn_details( $data, $voucher_no );
249
250 $wpdb->query( 'COMMIT' );
251 } catch ( Exception $e ) {
252 $wpdb->query( 'ROLLBACK' );
253
254 return new WP_error( 'invoice-exception', $e->getMessage() );
255 }
256
257 $invoice = erp_acct_get_invoice( $voucher_no );
258
259 $invoice['email'] = erp_get_people_email( $data['customer_id'] );
260
261 do_action( 'erp_acct_new_transaction_sales', $voucher_no, $invoice );
262
263 return $invoice;
264 }
265
266 /**
267 * Insert line items and details on invoice create
268 *
269 * @param array $invoice_data
270 * @param int $voucher_no
271 *
272 * @return void
273 */
274 function erp_acct_insert_invoice_details_and_tax( $invoice_data, $voucher_no, $contra = false ) {
275 global $wpdb;
276
277 $user_id = get_current_user_id();
278
279 $invoice_data['created_at'] = date( 'Y-m-d' );
280 $invoice_data['created_by'] = $user_id;
281 $invoice_data['updated_at'] = date( 'Y-m-d' );
282 $invoice_data['updated_by'] = $user_id;
283
284 $estimate_type = 1;
285 $draft = 1;
286 $tax_agency_details = [];
287
288 $items = $invoice_data['line_items'];
289
290 foreach ( $items as $item ) {
291 $sub_total = $item['qty'] * $item['unit_price'];
292
293 // insert into invoice details
294 $wpdb->insert(
295 $wpdb->prefix . 'erp_acct_invoice_details',
296 [
297 'trn_no' => $voucher_no,
298 'product_id' => $item['product_id'],
299 'qty' => $item['qty'],
300 'unit_price' => $item['unit_price'],
301 'discount' => $item['discount'],
302 'tax' => $item['tax'],
303 'item_total' => $sub_total,
304 'ecommerce_type' => ! empty( $item['ecommerce_type'] ) ? $item['ecommerce_type'] : null,
305 'created_at' => $invoice_data['created_at'],
306 'created_by' => $invoice_data['created_by'],
307 ]
308 );
309
310 $details_id = $wpdb->insert_id;
311
312 if ( $estimate_type === $invoice_data['estimate'] || $draft === $invoice_data['status'] ) {
313 continue;
314 }
315
316 if ( empty( $invoice_data['tax_rate_id'] ) && empty( $item['tax_cat_id'] ) ) {
317 $tax_rate_agency = ! empty( $item['tax_rate_agency'] ) ? $item['tax_rate_agency'] : null;
318 } else {
319 // calculate tax for every related agency
320 $tax_rate_agency = get_tax_rate_with_agency( $invoice_data['tax_rate_id'], $item['tax_cat_id'] );
321 }
322
323 if ( ! empty( $tax_rate_agency ) ) {
324 foreach ( $tax_rate_agency as $rate_agency ) {
325 /*==== calculate tax amount ====*/
326 $tax_amount = ( (float) $item['tax'] * (float) $rate_agency['tax_rate'] ) / (float) $item['tax_rate'];
327
328 if ( array_key_exists( $rate_agency['agency_id'], $tax_agency_details ) ) {
329 $tax_agency_details[ $rate_agency['agency_id'] ] += $tax_amount;
330 } else {
331 $tax_agency_details[ $rate_agency['agency_id'] ] = $tax_amount;
332 }
333
334 /*==== insert into invoice details tax ====*/
335 $wpdb->insert(
336 $wpdb->prefix . 'erp_acct_invoice_details_tax',
337 [
338 'invoice_details_id' => $details_id,
339 'agency_id' => $rate_agency['agency_id'],
340 'tax_rate' => $rate_agency['tax_rate'],
341 'tax_amount' => $tax_amount,
342 'created_at' => $invoice_data['created_at'],
343 'created_by' => $invoice_data['created_by'],
344 ]
345 );
346 }
347 }
348 }
349
350 if ( ! empty( $tax_agency_details ) ) {
351 // insert data into {$wpdb->prefix}erp_acct_tax_agency_details
352 foreach ( $tax_agency_details as $agency_id => $tax_agency_detail ) {
353 if ( $contra ) {
354 $debit = $invoice_data['tax'];
355 $credit = 0;
356 } else {
357 $debit = 0;
358 $credit = $tax_agency_detail;
359 }
360
361 $wpdb->insert(
362 $wpdb->prefix . 'erp_acct_tax_agency_details',
363 [
364 'agency_id' => $agency_id,
365 'trn_no' => $voucher_no,
366 'trn_date' => $invoice_data['trn_date'],
367 'particulars' => 'sales',
368 'debit' => $debit,
369 'credit' => $credit,
370 'created_at' => $invoice_data['created_at'],
371 'created_by' => $invoice_data['created_by'],
372 ]
373 );
374 }
375 }
376 }
377
378 /**
379 * Insert invoice account details
380 *
381 * @param array $invoice_data
382 * @param int $voucher_no
383 *
384 * @return void
385 */
386 function erp_acct_insert_invoice_account_details( $invoice_data, $voucher_no, $contra = false ) {
387 global $wpdb;
388
389 $user_id = get_current_user_id();
390
391 $invoice_data['created_at'] = date( 'Y-m-d H:i:s' );
392 $invoice_data['created_by'] = $user_id;
393 $invoice_data['updated_at'] = date( 'Y-m-d H:i:s' );
394 $invoice_data['updated_by'] = $user_id;
395
396 if ( $contra ) {
397 $invoice_no = $invoice_data['voucher_no'];
398 $debit = 0;
399 $credit = ( $invoice_data['amount'] - $invoice_data['discount'] ) + $invoice_data['tax'];
400 } else {
401 $invoice_no = $voucher_no;
402 $debit = ( $invoice_data['amount'] - $invoice_data['discount'] ) + $invoice_data['tax'];
403 $credit = 0;
404 }
405
406 $wpdb->insert(
407 $wpdb->prefix . 'erp_acct_invoice_account_details',
408 [
409 'invoice_no' => $invoice_no,
410 'trn_no' => $voucher_no,
411 'trn_date' => $invoice_data['trn_date'],
412 'particulars' => '',
413 'debit' => $debit,
414 'credit' => $credit,
415 'created_at' => $invoice_data['created_at'],
416 'created_by' => $invoice_data['created_by'],
417 'updated_at' => $invoice_data['created_at'],
418 'updated_by' => $invoice_data['created_by'],
419 ]
420 );
421 }
422
423 /**
424 * Update invoice data
425 *
426 * @param $data
427 * @param $invoice_no
428 *
429 * @return int
430 */
431 function erp_acct_update_invoice( $data, $invoice_no ) {
432 global $wpdb;
433
434 if ( 1 === $data['estimate'] && $data['convert'] ) {
435 erp_acct_convert_estimate_to_invoice( $data, $invoice_no );
436
437 return;
438 }
439
440 $user_id = get_current_user_id();
441 $voucher_no = null;
442
443 $data['created_at'] = date( 'Y-m-d H:i:s' );
444 $data['created_by'] = $user_id;
445 $data['updated_at'] = date( 'Y-m-d H:i:s' );
446 $data['updated_by'] = $user_id;
447
448 $estimate_type = 1;
449 $draft = 1;
450 $currency = erp_get_currency( true );
451
452 try {
453 $wpdb->query( 'START TRANSACTION' );
454
455 if ( $estimate_type === $data['estimate'] || $draft === $data['status'] ) {
456 erp_acct_update_draft_and_estimate( $data, $invoice_no );
457 } else {
458 // disable editing on old invoice
459 $wpdb->update( $wpdb->prefix . 'erp_acct_voucher_no', [ 'editable' => 0 ], [ 'id' => $invoice_no ] );
460
461 // insert contra voucher
462 $wpdb->insert(
463 $wpdb->prefix . 'erp_acct_voucher_no',
464 [
465 'type' => 'invoice',
466 'currency' => $currency,
467 'editable' => 0,
468 'created_at' => $data['created_at'],
469 'created_by' => $data['created_by'],
470 'updated_at' => $data['updated_at'],
471 'updated_by' => $data['updated_by'],
472 ]
473 );
474
475 $voucher_no = $wpdb->insert_id;
476
477 $old_invoice = erp_acct_get_invoice( $invoice_no );
478
479 // insert contra `erp_acct_invoices` (basically a duplication of row)
480 $wpdb->query( $wpdb->prepare( "CREATE TEMPORARY TABLE acct_tmptable SELECT * FROM {$wpdb->prefix}erp_acct_invoices WHERE voucher_no = %d", $invoice_no ) );
481 $wpdb->query(
482 $wpdb->prepare(
483 "UPDATE acct_tmptable SET id = %d, voucher_no = %d, particulars = 'Contra entry for voucher no \#%d', created_at = '%s'",
484 0,
485 $voucher_no,
486 $invoice_no,
487 $data['created_at']
488 )
489 );
490 $wpdb->query( "INSERT INTO {$wpdb->prefix}erp_acct_invoices SELECT * FROM acct_tmptable" );
491 $wpdb->query( 'DROP TABLE acct_tmptable' );
492
493 // change invoice status and other things
494 $status_closed = 7;
495 $wpdb->query(
496 $wpdb->prepare(
497 "UPDATE {$wpdb->prefix}erp_acct_invoices SET status = %d, updated_at ='%s', updated_by = %d WHERE voucher_no IN (%d, %d)",
498 $status_closed,
499 $data['updated_at'],
500 $user_id,
501 $invoice_no,
502 $voucher_no
503 )
504 );
505
506 // insert contra `erp_acct_invoice_details` AND `erp_acct_invoice_details_tax`
507 erp_acct_insert_invoice_details_and_tax( $old_invoice, $voucher_no, true );
508
509 // insert contra `erp_acct_invoice_account_details`
510 erp_acct_insert_invoice_account_details( $old_invoice, $voucher_no, true );
511
512 // insert contra `erp_acct_ledger_details`
513 erp_acct_insert_invoice_data_into_ledger( $old_invoice, $voucher_no, true );
514
515 // insert new invoice with edited data
516 $new_invoice = erp_acct_insert_invoice( $data );
517
518 do_action( 'erp_acct_after_sales_update', $data, $invoice_no );
519
520 $data['dr'] = $data['amount'];
521 $data['cr'] = 0;
522 erp_acct_update_data_into_people_trn_details( $data, $old_invoice['voucher_no'] );
523 }
524
525 $wpdb->query( 'COMMIT' );
526 } catch ( Exception $e ) {
527 $wpdb->query( 'ROLLBACK' );
528
529 return new WP_error( 'invoice-exception', $e->getMessage() );
530 }
531
532 return erp_acct_get_invoice( $new_invoice['voucher_no'] );
533 }
534
535 /**
536 * Convert estimate to invoice
537 *
538 * @param array $data
539 * @param int $invoice_no
540 *
541 * @return array
542 */
543 function erp_acct_convert_estimate_to_invoice( $data, $invoice_no ) {
544 global $wpdb;
545
546 $user_id = get_current_user_id();
547
548 $data['created_at'] = date( 'Y-m-d' );
549 $data['created_by'] = $user_id;
550 $data['updated_at'] = date( 'Y-m-d' );
551 $data['updated_by'] = $user_id;
552 $data['estimate'] = 0;
553
554 try {
555 $wpdb->query( 'START TRANSACTION' );
556
557 $invoice_data = erp_acct_get_formatted_invoice_data( $data, $invoice_no );
558
559 // erp_acct_invoices
560 $wpdb->update(
561 $wpdb->prefix . 'erp_acct_invoices',
562 [
563 'customer_id' => $invoice_data['customer_id'],
564 'customer_name' => $invoice_data['customer_name'],
565 'trn_date' => $invoice_data['trn_date'],
566 'due_date' => $invoice_data['due_date'],
567 'billing_address' => $invoice_data['billing_address'],
568 'amount' => $invoice_data['amount'],
569 'discount' => $invoice_data['discount'],
570 'discount_type' => $invoice_data['discount_type'],
571 'tax' => $invoice_data['tax'],
572 'estimate' => false,
573 'attachments' => $invoice_data['attachments'],
574 'status' => 2,
575 'particulars' => $invoice_data['particulars'],
576 'created_at' => $invoice_data['created_at'],
577 'created_by' => $invoice_data['created_by'],
578 ],
579 [ 'voucher_no' => $invoice_no ]
580 );
581
582 // remove data from erp_acct_invoice_details
583 $wpdb->delete( $wpdb->prefix . 'erp_acct_invoice_details', [ 'trn_no' => $invoice_no ] );
584
585 // insert data into erp_acct_invoice_details
586 erp_acct_insert_invoice_details_and_tax( $invoice_data, $invoice_no );
587
588 erp_acct_insert_invoice_account_details( $invoice_data, $invoice_no );
589
590 erp_acct_insert_invoice_data_into_ledger( $invoice_data, $invoice_no );
591
592 do_action( 'erp_acct_after_sales_create', $data, $invoice_no );
593
594 $data['dr'] = $invoice_data['amount'];
595 $data['cr'] = 0;
596 erp_acct_insert_data_into_people_trn_details( $data, $invoice_no );
597
598 $wpdb->query( 'COMMIT' );
599 } catch ( Exception $e ) {
600 $wpdb->query( 'ROLLBACK' );
601
602 return new WP_error( 'invoice-exception', $e->getMessage() );
603 }
604
605 $invoice = erp_acct_get_invoice( $invoice_no );
606
607 $invoice['email'] = erp_get_people_email( $data['customer_id'] );
608
609 do_action( 'erp_acct_new_transaction_sales', $invoice_no, $invoice );
610
611 return $invoice;
612 }
613
614 /**
615 * Update draft & estimate
616 *
617 * @param array $data
618 * @param int $invoice_no
619 *
620 * @return void
621 */
622 function erp_acct_update_draft_and_estimate( $data, $invoice_no ) {
623 global $wpdb;
624
625 $invoice_data = erp_acct_get_formatted_invoice_data( $data, $invoice_no );
626
627 $wpdb->update( $wpdb->prefix . 'erp_acct_invoices', [
628 'customer_id' => $invoice_data['customer_id'],
629 'customer_name' => $invoice_data['customer_name'],
630 'trn_date' => $invoice_data['trn_date'],
631 'due_date' => $invoice_data['due_date'],
632 'billing_address' => $invoice_data['billing_address'],
633 'amount' => $invoice_data['amount'],
634 'discount' => $invoice_data['discount'],
635 'discount_type' => $invoice_data['discount_type'],
636 'tax' => $invoice_data['tax'],
637 'estimate' => $invoice_data['estimate'],
638 'attachments' => $invoice_data['attachments'],
639 'status' => $invoice_data['status'],
640 'particulars' => $invoice_data['particulars'],
641 'updated_at' => $invoice_data['updated_at'],
642 'updated_by' => $invoice_data['updated_by'],
643 ], [ 'voucher_no' => $invoice_no ] );
644
645 /*
646 *? We can't update `invoice_details` directly
647 *? suppose there were 5 detail rows previously
648 *? but on update there may be 2 detail rows
649 *? that's why we can't update because the foreach will iterate only 2 times, not 5 times
650 *? so, remove previous rows to insert new rows
651 */
652 $wpdb->delete( $wpdb->prefix . 'erp_acct_invoice_details', [ 'trn_no' => $invoice_no ] );
653
654 erp_acct_insert_invoice_details_and_tax( $invoice_data, $invoice_no );
655 }
656
657 /**
658 * Get formatted invoice data
659 *
660 * @param $data
661 * @param $voucher_no
662 *
663 * @return mixed
664 */
665 function erp_acct_get_formatted_invoice_data( $data, $voucher_no ) {
666 $invoice_data = [];
667
668 // We can pass the name from view... to reduce DB query load
669 if ( empty( $data['customer_name'] ) ) {
670 $customer = erp_get_people( $data['customer_id'] );
671 $customer_name = $customer->first_name . ' ' . $customer->last_name;
672 } else {
673 $customer_name = $data['customer_name'];
674 }
675
676 $invoice_data['voucher_no'] = ! empty( $voucher_no ) ? $voucher_no : 0;
677 $invoice_data['customer_id'] = isset( $data['customer_id'] ) ? $data['customer_id'] : null;
678 $invoice_data['customer_name'] = $customer_name;
679 $invoice_data['trn_date'] = isset( $data['date'] ) ? $data['date'] : date( 'Y-m-d' );
680 $invoice_data['due_date'] = isset( $data['due_date'] ) ? $data['due_date'] : date( 'Y-m-d' );
681 $invoice_data['billing_address'] = isset( $data['billing_address'] ) ? maybe_serialize( $data['billing_address'] ) : '';
682 $invoice_data['amount'] = isset( $data['amount'] ) ? $data['amount'] : 0;
683 $invoice_data['discount'] = isset( $data['discount'] ) ? $data['discount'] : 0;
684 $invoice_data['discount_type'] = isset( $data['discount_type'] ) ? $data['discount_type'] : null;
685 $invoice_data['tax_rate_id'] = isset( $data['tax_rate_id'] ) ? $data['tax_rate_id'] : 0;
686 $invoice_data['line_items'] = isset( $data['line_items'] ) ? $data['line_items'] : [];
687 $invoice_data['trn_by'] = isset( $data['trn_by'] ) ? $data['trn_by'] : '';
688 $invoice_data['tax'] = isset( $data['tax'] ) ? $data['tax'] : 0;
689 $invoice_data['attachments'] = ! empty( $data['attachments'] ) ? $data['attachments'] : '';
690 $invoice_data['status'] = isset( $data['status'] ) ? $data['status'] : 1;
691 // translators: %s: voucher_no
692 $invoice_data['particulars'] = ! empty( $data['particulars'] ) ? $data['particulars'] : sprintf( __( 'Invoice created with voucher no %s', 'erp' ), $voucher_no );
693 $invoice_data['estimate'] = isset( $data['estimate'] ) ? $data['estimate'] : 1;
694 $invoice_data['created_at'] = isset( $data['created_at'] ) ? $data['created_at'] : null;
695 $invoice_data['created_by'] = isset( $data['created_by'] ) ? $data['created_by'] : null;
696 $invoice_data['updated_at'] = isset( $data['updated_at'] ) ? $data['updated_at'] : null;
697 $invoice_data['updated_by'] = isset( $data['updated_by'] ) ? $data['updated_by'] : null;
698
699 $draft = 1;
700 $pending = 3;
701
702 if ( ! empty( $data['estimate'] ) && $data['status'] !== $draft ) {
703 $invoice_data['status'] = $pending;
704 }
705
706 return $invoice_data;
707 }
708
709 /**
710 * Void an invoice
711 *
712 * @param $invoice_no
713 *
714 * @return void
715 */
716 function erp_acct_void_invoice( $invoice_no ) {
717 global $wpdb;
718
719 if ( ! $invoice_no ) {
720 return;
721 }
722
723 $wpdb->update(
724 $wpdb->prefix . 'erp_acct_invoices',
725 [
726 'status' => 8,
727 ],
728 [ 'voucher_no' => $invoice_no ]
729 );
730
731 $wpdb->delete( $wpdb->prefix . 'erp_acct_ledger_details', [ 'trn_no' => $invoice_no ] );
732 $wpdb->delete( $wpdb->prefix . 'erp_acct_invoice_account_details', [ 'invoice_no' => $invoice_no ] );
733
734 $results = $wpdb->get_results(
735 $wpdb->prepare(
736 "SELECT
737 inv_detail_tax.id
738 FROM {$wpdb->prefix}erp_acct_invoice_details_tax as inv_detail_tax
739 LEFT JOIN {$wpdb->prefix}erp_acct_invoice_details as inv_detail ON inv_detail_tax.invoice_details_id = inv_detail.id
740 LEFT JOIN {$wpdb->prefix}erp_acct_invoices as invoice ON inv_detail.trn_no = invoice.voucher_no
741 WHERE inv_detail.trn_no = %d",
742 $invoice_no
743 ),
744 ARRAY_A
745 );
746
747 foreach ( $results as $result ) {
748 $wpdb->delete( $wpdb->prefix . 'erp_acct_invoice_details_tax', [ 'id' => $result['id'] ] );
749 }
750
751 $wpdb->delete( $wpdb->prefix . 'erp_acct_tax_agency_details', [ 'trn_no' => $invoice_no ] );
752 }
753
754 /**
755 * Tax category with agency
756 */
757 function get_tax_rate_with_agency( $tax_id, $tax_cat_id ) {
758 global $wpdb;
759
760 return $wpdb->get_results(
761 $wpdb->prepare(
762 "SELECT agency_id, tax_rate FROM {$wpdb->prefix}erp_acct_tax_cat_agency where tax_id = %d and tax_cat_id = %d",
763 absint( $tax_id ),
764 absint( $tax_cat_id )
765 ),
766 ARRAY_A
767 );
768 }
769
770 /**
771 * Insert invoice/s data into ledger
772 *
773 * @param array $invoice_data
774 *
775 * @return mixed
776 */
777 function erp_acct_insert_invoice_data_into_ledger( $invoice_data, $voucher_no = 0, $contra = false ) {
778 global $wpdb;
779
780 $user_id = get_current_user_id();
781 $date = date( 'Y-m-d H:i:s' );
782
783 $ledger_map = \WeDevs\ERP\Accounting\Includes\Classes\Ledger_Map::get_instance();
784
785 $sales_ledger_id = $ledger_map->get_ledger_id_by_slug( 'sales_revenue' );
786 $sales_discount_ledger_id = $ledger_map->get_ledger_id_by_slug( 'sales_discount' );
787
788 if ( $contra ) {
789 $trn_no = $voucher_no;
790
791 $discount_debit = 0;
792 $sales_credit = 0;
793
794 $sales_debit = $invoice_data['amount'];
795 $discount_credit = $invoice_data['discount'];
796 } else {
797 $trn_no = $invoice_data['voucher_no'];
798
799 $sales_debit = 0;
800 $discount_credit = 0;
801
802 $sales_credit = $invoice_data['amount'];
803 $discount_debit = $invoice_data['discount'];
804 }
805
806 // insert amount in ledger_details
807 $wpdb->insert(
808 $wpdb->prefix . 'erp_acct_ledger_details',
809 [
810 'ledger_id' => $sales_ledger_id,
811 'trn_no' => $trn_no,
812 'particulars' => $invoice_data['particulars'],
813 'debit' => $sales_debit,
814 'credit' => $sales_credit,
815 'trn_date' => $invoice_data['trn_date'],
816 'created_at' => $date,
817 'created_by' => $user_id,
818 'updated_at' => $date,
819 'updated_by' => $user_id,
820 ]
821 );
822
823 // insert discount in ledger_details
824 $wpdb->insert(
825 $wpdb->prefix . 'erp_acct_ledger_details',
826 [
827 'ledger_id' => $sales_discount_ledger_id,
828 'trn_no' => $trn_no,
829 'particulars' => $invoice_data['particulars'],
830 'debit' => $discount_debit,
831 'credit' => $discount_credit,
832 'trn_date' => $invoice_data['trn_date'],
833 'created_at' => $date,
834 'created_by' => $user_id,
835 'updated_at' => $date,
836 'updated_by' => $user_id,
837 ]
838 );
839 }
840
841 /**
842 * Update invoice/s data into ledger
843 *
844 * @param array $invoice_data
845 *
846 * @return mixed
847 */
848 function erp_acct_update_invoice_data_in_ledger( $invoice_data, $invoice_no ) {
849 global $wpdb;
850
851 // Update amount in ledger_details
852 $wpdb->update(
853 $wpdb->prefix . 'erp_acct_ledger_details',
854 [
855 'particulars' => $invoice_data['particulars'],
856 'credit' => $invoice_data['amount'],
857 'trn_date' => $invoice_data['trn_date'],
858 'updated_at' => $invoice_data['updated_at'],
859 'updated_by' => $invoice_data['updated_by'],
860 ],
861 [
862 'trn_no' => $invoice_no,
863 ]
864 );
865
866 // Update discount in ledger_details
867 $wpdb->update(
868 $wpdb->prefix . 'erp_acct_ledger_details',
869 [
870 'particulars' => $invoice_data['particulars'],
871 'debit' => $invoice_data['discount'],
872 'trn_date' => $invoice_data['trn_date'],
873 'updated_at' => $invoice_data['updated_at'],
874 'updated_by' => $invoice_data['updated_by'],
875 ],
876 [
877 'trn_no' => $invoice_no,
878 ]
879 );
880 }
881
882 /**
883 * Get Invoice count
884 *
885 * @return int
886 */
887 function erp_acct_get_invoice_count() {
888 global $wpdb;
889
890 $row = $wpdb->get_row( 'SELECT COUNT(*) as count FROM ' . $wpdb->prefix . 'erp_acct_invoices' );
891
892 return $row->count;
893 }
894
895 /**
896 * Receive payments with due from a customer
897 *
898 * @return mixed
899 */
900 function erp_acct_receive_payments_from_customer( $args = [] ) {
901 global $wpdb;
902
903 $defaults = [
904 'number' => 20,
905 'offset' => 0,
906 'orderby' => 'id',
907 'order' => 'DESC',
908 'count' => false,
909 's' => '',
910 ];
911
912 $args = wp_parse_args( $args, $defaults );
913
914 $limit = '';
915
916 if ( '-1' === $args['number'] ) {
917 $limit = "LIMIT {$args['number']} OFFSET {$args['offset']}";
918 }
919
920 $invoices = "{$wpdb->prefix}erp_acct_invoices";
921 $invoice_act_details = "{$wpdb->prefix}erp_acct_invoice_account_details";
922 $items = $args['count'] ? ' COUNT( id ) as total_number ' : ' id, voucher_no, due_date, (amount + tax - discount) as amount, invs.due as due ';
923
924 $query = $wpdb->prepare(
925 "SELECT $items FROM $invoices as invoice INNER JOIN
926 (SELECT invoice_no, SUM( ia.debit - ia.credit) as due
927 FROM $invoice_act_details as ia
928 GROUP BY ia.invoice_no
929 HAVING due > 0) as invs
930 ON invoice.voucher_no = invs.invoice_no
931 WHERE invoice.customer_id = %d AND invoice.status != 1 AND invoice.estimate != 1
932 ORDER BY %s %s $limit",
933 $args['people_id'],
934 $args['orderby'],
935 $args['order']
936 );
937
938 if ( $args['count'] ) {
939 return $wpdb->get_var( $query );
940 }
941
942 return $wpdb->get_results( $query, ARRAY_A );
943 }
944
945 /**
946 * Get due of a bill
947 *
948 * @param $bill_no
949 *
950 * @return int
951 */
952 function erp_acct_get_due_payment( $invoice_no ) {
953 global $wpdb;
954
955 $result = $wpdb->get_row( $wpdb->prepare( "SELECT invoice_no, SUM( ia.debit - ia.credit) as due FROM {$wpdb->prefix}erp_acct_invoice_account_details as ia WHERE ia.invoice_no = %d GROUP BY ia.invoice_no", $invoice_no ), ARRAY_A );
956
957 return $result['due'];
958 }
959
960 /**
961 * Get recievables from given date
962 *
963 * @param $from String
964 * @param $to String
965 *
966 * @return array|object|null
967 */
968 function erp_acct_get_recievables( $from, $to ) {
969 global $wpdb;
970
971 $from_date = date( 'Y-m-d', strtotime( $from ) );
972 $to_date = date( 'Y-m-d', strtotime( $to ) );
973
974 $invoices = $wpdb->prefix . 'erp_acct_invoices';
975 $invoices_acct_details = $wpdb->prefix . 'erp_acct_invoice_account_details';
976
977 $query = $wpdb->prepare(
978 "Select voucher_no, SUM(ad.debit - ad.credit) as due, due_date
979 FROM $invoices LEFT JOIN $invoices_acct_details as ad
980 ON ad.invoice_no = voucher_no where due_date
981 BETWEEN %s and %s Group BY voucher_no Having due > 0 ",
982 $from_date,
983 $to_date
984 );
985
986 $results = $wpdb->get_results( $query, ARRAY_A );
987
988 return $results;
989 }
990
991 /**
992 * Get Dashboard Overview details
993 */
994 function erp_acct_get_recievables_overview() {
995 // get dates till coming 90 days
996 $from_date = date( 'Y-m-d' );
997 $to_date = date( 'Y-m-d', strtotime( '+90 day', strtotime( $from_date ) ) );
998
999 $data = [];
1000 $amount = [
1001 'first' => 0,
1002 'second' => 0,
1003 'third' => 0,
1004 ];
1005
1006 $result = erp_acct_get_recievables( $from_date, $to_date );
1007
1008 if ( ! empty( $result ) ) {
1009 $from_date = new DateTime( $from_date );
1010
1011 foreach ( $result as $item_data ) {
1012 $item = (object) $item_data;
1013 $later = new DateTime( $item->due_date );
1014 $diff = $later->diff( $from_date )->format( '%a' );
1015
1016 //segment by date difference
1017 switch ( $diff ) {
1018
1019 case $diff === 0:
1020 $data['first'][] = $item_data;
1021 $amount['first'] = $amount['first'] + $item->due;
1022 break;
1023
1024 case $diff <= 30:
1025 $data['first'][] = $item_data;
1026 $amount['first'] = $amount['first'] + $item->due;
1027 break;
1028
1029 case $diff <= 60:
1030 $data['second'][] = $item_data;
1031 $amount['second'] = $amount['second'] + $item->due;
1032 break;
1033
1034 case $diff <= 90:
1035 $data['third'][] = $item_data;
1036 $amount['third'] = $amount['third'] + $item->due;
1037 break;
1038
1039 default:
1040 }
1041 }
1042 }
1043
1044 return [
1045 'data' => $data,
1046 'amount' => $amount,
1047 ];
1048 }
1049
1050 /**
1051 * Get due of an invoice
1052 *
1053 * @param $invoice_no
1054 *
1055 * @return int
1056 */
1057 function erp_acct_get_invoice_due( $invoice_no ) {
1058 global $wpdb;
1059
1060 $result = $wpdb->get_row( $wpdb->prepare( "SELECT invoice_no, SUM( ia.debit - ia.credit) as due FROM {$wpdb->prefix}erp_acct_invoice_account_details as ia WHERE ia.invoice_no = %d GROUP BY ia.invoice_no", $invoice_no ), ARRAY_A );
1061
1062 return $result['due'];
1063 }
1064