Compare commits

...

153 Commits

Author SHA1 Message Date
Dillon Ngo 295072153d Merge branch 'dillon/70-laravel-vapor' into 'master'
Laravel Vapor - Disable all schedule commands

See merge request CIEFWorldwideSdnBhd/shipping-portal!237
2024-09-13 17:43:28 +00:00
Dillon Ngo e90aebd107 Laravel Vapor - Disable all schedule commands 2024-09-14 00:03:22 +08:00
edmondlang b7c6c13bde fix payment-and-billing vue console error 2024-09-01 19:58:51 +08:00
edmondlang 01d666d92a fix payment-and-billing vue console error 2024-09-01 19:48:44 +08:00
Dillon Ngo b3973b52d1 Merge branch 'dillon/46.15-onhold-list-anomaly-fix' into 'master'
Data patch for a problem reported about an item not suppose to appear in ...

See merge request CIEFWorldwideSdnBhd/shipping-portal!236
2024-08-17 00:41:10 +00:00
Dillon Ngo 694876c31f Data patch for a problem reported about an item not suppose to appear in export/on-hold-packing-list 2024-08-17 08:39:19 +08:00
edmondlang 499782bbec update /export/aging-list, add 'is credit term customer' column 2024-08-04 00:39:15 +08:00
edmondlang f3eca3dd89 Export Group Transactions CSV 2024-08-03 23:04:47 +08:00
Aqqil Azman 9c4c472a40 fix wrong decimal round off 2024-08-01 09:37:22 +08:00
Aqqil Azman a67afdc0ff changed decimal points to 2 2024-08-01 09:34:14 +08:00
edmondlang b30cdaba84 fix export/aging-list, it has duplicated rows due to unnecessary joins 2024-07-26 00:58:49 +08:00
Dillon Ngo 9ac0ca45be Merge branch 'dillon/46.14-data-patch' into 'master'
Data patch for storage invoice

See merge request CIEFWorldwideSdnBhd/shipping-portal!235
2024-07-22 23:07:16 +00:00
Dillon Ngo 3fe98b24a4 Data patch for storage invoice 2024-07-23 07:06:34 +08:00
Dillon Ngo 7e7e43ee9c Merge branch 'dillon/46.14-data-patch' into 'master'
Data patch for storage invoice

See merge request CIEFWorldwideSdnBhd/shipping-portal!234
2024-07-22 23:00:09 +00:00
Dillon Ngo 39892a0938 Data patch for storage invoice 2024-07-23 06:58:10 +08:00
edmondlang 0ffe81d1cf update call wac webhook 2024-07-19 00:35:38 +08:00
edmondlang 5fd6e6a528 update call wac webhook 2024-07-19 00:28:51 +08:00
edmondlang 09ede52ecf update call wac webhook 2024-07-18 23:59:23 +08:00
edmondlang e5d6f3f581 wac webhook 2024-07-18 23:11:04 +08:00
edmondlang aa65b333c8 update permits reminder 2024-07-17 18:13:50 +08:00
edmondlang 656c50e047 update permits reminder 2024-07-17 18:11:34 +08:00
edmondlang 275157b243 update permits reminder 2024-07-17 17:49:33 +08:00
edmondlang 0c9ffd2bf7 call wac webhook 2024-07-06 11:24:54 +08:00
edmondlang d4dcae155d add command to fix missing packinglist 2024-07-03 12:56:16 +08:00
edmondlang bad8cadb3a remove edmond email from permits reminder 2024-07-02 21:12:52 +08:00
edmondlang 3af32cc6e7 remove command fix missing packinglist 2024-07-02 20:57:27 +08:00
edmondlang 34bab6a45c fix missing packinglist 2024-07-02 20:52:06 +08:00
edmondlang dd1eae1ea2 fix missing packinglist 2024-07-02 13:02:16 +08:00
Dillon Ngo b46bfb38bf Merge branch 'dillon/62.3-fixes' into 'master'
Quick fix on https://izyim.cief-malaysia.com/order/show/245904319

See merge request CIEFWorldwideSdnBhd/shipping-portal!233
2024-07-01 07:01:20 +00:00
Dillon Ngo 19c4ee4fda Quick fix 2024-07-01 15:00:14 +08:00
Omair Saleh 13f13c7a0e export based on payment date 2024-07-01 13:46:12 +08:00
Omair Saleh ffa63f611b export based on payment date 2024-07-01 13:42:12 +08:00
Omair Saleh 82675ab80c export based on payment date 2024-07-01 13:40:14 +08:00
Omair Saleh aac04c4135 export based on payment date 2024-07-01 13:17:22 +08:00
Omair Saleh 56dca799f8 export based on payment date 2024-07-01 13:13:57 +08:00
Omair Saleh 42d8f811c6 export based on payment date 2024-07-01 13:13:15 +08:00
Omair Saleh 030136fd03 Merge remote-tracking branch 'origin/master' 2024-07-01 12:57:42 +08:00
Omair Saleh c8b6e271e3 export based on payment date 2024-07-01 12:57:23 +08:00
Dillon Ngo f93997b858 Merge branch 'dillon/62.2-benchmarking-yd-api-cron-job' into 'master'
Benchmark cron job for YD API call

See merge request CIEFWorldwideSdnBhd/shipping-portal!232
2024-06-29 07:33:25 +00:00
Dillon Ngo 6fbc23d7c4 Benchmark cron job for YD API call 2024-06-29 15:32:32 +08:00
Dillon Ngo e0a264d58f Merge branch 'dillon/62.2-benchmarking-yd-api-cron-job' into 'master'
Benchmark cron job for YD API call

See merge request CIEFWorldwideSdnBhd/shipping-portal!231
2024-06-29 06:01:54 +00:00
Dillon Ngo fec20b0947 Benchmark cron job for YD API call 2024-06-29 14:01:13 +08:00
Dillon Ngo 0bf61d93c5 Merge branch 'dillon/62.2-benchmarking-yd-api-cron-job' into 'master'
Benchmark cron job for YD API call

See merge request CIEFWorldwideSdnBhd/shipping-portal!230
2024-06-29 03:21:24 +00:00
Dillon Ngo bea24fb48e Benchmark cron job for YD API call 2024-06-29 11:19:55 +08:00
edmondlang 17a4522f50 add transaction counter 2024-06-26 23:52:25 +08:00
edmondlang dfedb99c13 fix bug: customer pay full amount but payment cant go through due to rounding up issue 2024-06-26 23:43:40 +08:00
edmondlang 669cac9bd4 fix-approved-payment-failed-group 2024-06-26 13:47:58 +08:00
edmondlang d7625aece7 update group-transaction-with-completed-payments 2024-06-26 13:07:30 +08:00
edmondlang a36c5c3063 show group-transaction-with-completed-payments 2024-06-26 12:47:44 +08:00
Dillon Ngo fd75da5778 Merge branch 'dillon/51.5-vue-store-update' into 'master'
A minor fix to not allow non-admin user to see admin data by navigating to the...

See merge request CIEFWorldwideSdnBhd/shipping-portal!229
2024-06-17 00:58:05 +00:00
Dillon Ngo b593605c72 A minor fix to not allow non-admin user to see admin data by navigating to the url /payment-and-billing-2 2024-06-17 08:51:09 +08:00
Dillon Ngo 8d75012390 Merge branch 'dillon/62.1-fixes' into 'master'
Reinstate original code after data patch

See merge request CIEFWorldwideSdnBhd/shipping-portal!228
2024-06-16 06:14:47 +00:00
Dillon Ngo 2d98757c1c Data patch meant to fix a group payment with group id 609 and transaction id 16803 that got stuck after making paying 2024-06-16 14:13:09 +08:00
Dillon Ngo b329f232f2 Merge branch 'dillon/62.1-fixes' into 'master'
Data patch meant to fix a group payment with group id 609 and transaction id...

See merge request CIEFWorldwideSdnBhd/shipping-portal!227
2024-06-16 06:06:54 +00:00
Dillon Ngo 713922d2ee Data patch meant to fix a group payment with group id 609 and transaction id 16803 that got stuck after making paying 2024-06-16 14:04:16 +08:00
Dillon Ngo 74d11f8305 Merge branch 'dillon/62.1-fixes' into 'master'
Fix a rounding problem in group payment which will result in goods cannot be...

See merge request CIEFWorldwideSdnBhd/shipping-portal!226
2024-06-14 21:58:25 +00:00
Dillon Ngo ea920717a1 Fix a rounding problem in group payment which will result in goods cannot be released through CallbackBillplzLogic 2024-06-15 05:56:23 +08:00
Dillon Ngo e1b49f6be0 Merge branch 'dillon/46.13-missing-payment-proof-feature' into 'master'
Restore missing payment proof feature

See merge request CIEFWorldwideSdnBhd/shipping-portal!224
2024-06-14 21:45:15 +00:00
edmondlang 1be405ca93 fix error message in admin dashbaord 2024-06-13 23:47:43 +08:00
Dillon Ngo e63e3e707f 'Your Payment Proof' feature missing for order with storage invoices through group payment 2024-06-09 12:44:19 +08:00
Dillon Ngo aeaec32dcb 'Your Payment Proof' feature missing for order with storage invoices through group payment 2024-06-09 12:16:18 +08:00
Dillon Ngo b000c25e66 'Your Payment Proof' feature missing for order with storage invoices through group payment 2024-06-09 12:00:19 +08:00
edmondlang 1e9af59b91 test gtag 2024-06-07 17:40:48 +08:00
edmondlang a392b8a5eb test ga4 2024-06-07 17:35:57 +08:00
edmondlang 30edc3eaba updates RescheduleContainerLogic delete all the previous container transport schedule, then create a new one 2024-05-30 23:08:05 +08:00
edmondlang b0eca18343 show packinglist reference for admin only 2024-05-27 00:53:48 +08:00
Dillon Ngo 88e1b43f19 Merge branch 'dillon/46.11-warehouse-storage-related-fixes' into 'master'
Inserted logs for monitoring warehouse storage charges

See merge request CIEFWorldwideSdnBhd/shipping-portal!223
2024-05-26 08:58:20 +00:00
omair saleh fc9f6d6dde Merge branch 'fulfilment-banner' into 'master'
fix segment requried contact bug

See merge request CIEFWorldwideSdnBhd/shipping-portal!222
2024-05-24 06:52:53 +00:00
Jia Sheng 141dab15b6 fix segment requried contact bug 2024-05-23 11:44:18 +08:00
edmondlang c9ac435e37 update confirmation message 2024-05-21 01:33:24 +08:00
edmondlang 6d01bdfb60 Merge branch 'fulfilment-banner' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal 2024-05-21 01:27:47 +08:00
edmondlang e2dc83e441 add segments in the menu bar. 2024-05-21 01:26:09 +08:00
Jia Sheng e9daadaf60 not interested confirmation 2024-05-21 01:25:14 +08:00
edmondlang 7cace7e800 Merge branch 'master' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal into fulfilment-banner 2024-05-21 00:54:21 +08:00
Jia Sheng 64fa56c02f fulfilment banner flow update, segment user index list 2024-05-20 22:51:22 +08:00
Dillon Ngo 3f962e774d Merge branch 'dillon/51.5-vue-store-update' into 'master'
Vue Polling - Fix minor typo error

See merge request CIEFWorldwideSdnBhd/shipping-portal!221
2024-05-19 19:14:56 +00:00
Dillon Ngo 4ba2144f02 Vue Polling - Fix minor typo error 2024-05-20 02:28:49 +08:00
Dillon Ngo 69f2b384cf Merge branch 'dillon/51.5-vue-store-update' into 'master'
Vue Polling - Refactor code to use vuex

See merge request CIEFWorldwideSdnBhd/shipping-portal!220
2024-05-19 16:06:00 +00:00
Dillon Ngo f1a35299e6 Vue Polling - Remove unnecessary code 2024-05-19 23:48:40 +08:00
Dillon Ngo b15866ea16 Vue Polling - Remove comments in code 2024-05-19 23:47:00 +08:00
Dillon Ngo bd42f7213a Vue Polling - Refactor code to use vuex 2024-05-18 18:55:21 +08:00
jiasheng224 2d92b1a8ab fulfilment banner module 2024-05-08 18:30:12 +08:00
Dillon Ngo 06e3049e50 Debugging 2024-05-08 03:03:58 +08:00
Dillon Ngo b0fa73a12b Debugging 2024-05-08 01:52:51 +08:00
Dillon Ngo 5d66761465 Merge branch 'dillon/60-shipping-portal-fixes' into 'master'
Deleted a file that should no longer be used

See merge request CIEFWorldwideSdnBhd/shipping-portal!219
2024-05-07 16:53:13 +00:00
Dillon Ngo 1ac4615c99 Delete a file that should no longer be used 2024-05-08 00:52:33 +08:00
Dillon Ngo 590960b5fd Merge branch 'dillon/60-shipping-portal-fixes' into 'master'
Move a file that should no longer be used (deprecated)

See merge request CIEFWorldwideSdnBhd/shipping-portal!218
2024-05-07 16:50:42 +00:00
Dillon Ngo 5f24f58480 Move a file that should no longer be used 2024-05-08 00:48:15 +08:00
edmondlang 4dec576aa0 Merge branch 'master' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal 2024-05-08 00:31:22 +08:00
edmondlang 4a41c89024 fix MappableTransactionWithDetailsResource.php 2024-05-08 00:30:01 +08:00
Dillon Ngo a0997a4649 Merge branch 'dillon/60-shipping-portal-fixes' into 'master'
Fix a problem with inefficient query discovered from Laravel Vapor integration (payment and billing page, pending payment tab)

See merge request CIEFWorldwideSdnBhd/shipping-portal!217
2024-05-07 16:27:55 +00:00
Dillon Ngo b90dae882e Fix a problem with inefficient query discovered from Laravel Vapor integration 2024-05-08 00:26:12 +08:00
edmondlang f361a7e185 fix MappableTransactionWithDetailsResource.php 2024-05-08 00:24:22 +08:00
Dillon Ngo 29619625d1 Merge branch 'dillon/46.11-warehouse-storage-related-fixes' into 'master'
Fix multipe interface user experience (UX) problem originally reported by Yien

See merge request CIEFWorldwideSdnBhd/shipping-portal!216
2024-05-07 16:15:11 +00:00
Dillon Ngo 1705b5c2e5 Fix a front end UX problem reported by Yien 2024-05-06 13:19:23 +08:00
Dillon Ngo 1813783d87 Merge branch 'dillon/60-shipping-portal-fixes' into 'master'
Fix emails cannot be sent with attachment

See merge request CIEFWorldwideSdnBhd/shipping-portal!215
2024-05-04 09:53:01 +00:00
Dillon Ngo 46a0e670ba Fix emails cannot be sent with attachment 2024-05-04 17:51:57 +08:00
Omair Saleh dcec8cb33b update payment export itemization 2024-04-18 02:27:35 +08:00
Omair Saleh 75037f6192 update payment export itemization 2024-04-18 02:22:30 +08:00
Omair Saleh 675324081b update payment export itemization 2024-04-18 02:21:30 +08:00
Omair Saleh a959e83f5b update payment export itemization 2024-04-18 02:19:05 +08:00
Omair Saleh 2802e3c366 update payment export itemization 2024-04-18 02:17:43 +08:00
Omair Saleh aac2a0666a update payment export itemization 2024-04-18 02:16:13 +08:00
Omair Saleh 19efba4b89 update payment export itemization 2024-04-18 02:13:43 +08:00
Omair Saleh 7d70c800db update payment export itemization 2024-04-18 02:12:55 +08:00
Omair Saleh 863dc94dad Revert "Revert "Merge branch 'dillon/58-shipping-portal-sst' into 'master'""
This reverts commit a49920973e.
2024-04-18 02:09:16 +08:00
Omair Saleh 98e45e27e3 update payment export itemization 2024-04-18 02:04:56 +08:00
Omair Saleh df2916594b remove invoice date adjuster 2024-04-18 01:14:39 +08:00
Omair Saleh a49920973e Revert "Merge branch 'dillon/58-shipping-portal-sst' into 'master'"
This reverts commit 7cd376a9b5, reversing
changes made to 6176135f85.
2024-04-16 13:21:07 +08:00
Omair Saleh d129f8f6bd clear wrong date invoice from april to march 2024-04-16 13:17:23 +08:00
Omair Saleh 55a82e52a4 Merge remote-tracking branch 'origin/master' 2024-04-16 13:02:53 +08:00
Omair Saleh 1d4ac80b8f change sst invoice format 2024-04-16 13:02:40 +08:00
omair saleh 7cd376a9b5 Merge branch 'dillon/58-shipping-portal-sst' into 'master'
Dillon/58 shipping portal sst

See merge request CIEFWorldwideSdnBhd/shipping-portal!213
2024-04-16 04:51:39 +00:00
Omair Saleh c7e3091f9b change sst invoice format 2024-04-16 12:49:46 +08:00
Omair Saleh 28feba2ae9 change sst invoice format 2024-04-16 12:39:44 +08:00
edmondlang 6176135f85 fix qr api issue 2024-04-10 23:17:05 +08:00
edmondlang 5f529965da Merge branch 'qr-api-issue' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal 2024-04-10 22:27:29 +08:00
edmondlang 924f4f2ace fix qr api issue 2024-04-10 22:27:09 +08:00
edmondlang 6369282b38 update qr code config 2024-04-10 18:22:11 +08:00
jiasheng224 1991cf2c57 fix qr api issue 2024-04-09 17:42:10 +08:00
jiasheng224 5f5bbe0c8c update wallet balance when credit or debit to wallet balance 2024-04-09 16:58:12 +08:00
Dillon Ngo a809aec9a7 SST Impelemtation 2024-04-07 10:22:12 +08:00
Dillon Ngo 68ce8373e9 SST Impelemtation 2024-04-07 09:27:09 +08:00
Omair Saleh e5347f56b7 change bank in details 2024-04-04 08:22:47 +08:00
Omair Saleh 6673a44137 fix invoice date for april 2024-04-03 12:22:32 +08:00
Omair Saleh 4d3345f613 Merge remote-tracking branch 'origin/master' 2024-04-03 12:17:54 +08:00
Omair Saleh 60b8657773 fix invoice date for april 2024-04-03 12:17:40 +08:00
edmondlang 9f7a8d0d4b hide deletePaidGroupTransaction 2024-03-31 23:12:38 +08:00
edmondlang a87ab6c27f logging delete paid group transaction 2024-03-31 21:41:42 +08:00
edmondlang 462a4776cd delete paid group transaction 2024-03-31 17:36:12 +08:00
edmondlang 7e029c5610 delete paid group transaction 2024-03-30 00:49:55 +08:00
edmondlang 0719285286 show all extra payments from customer 2024-03-29 00:59:30 +08:00
edmondlang 64400d1524 fix ExportsCustomersWalletTransactionHistory 2024-03-29 00:06:22 +08:00
edmondlang be0279f56b fix ExportsCustomersWalletTransactionHistory 2024-03-29 00:02:13 +08:00
Dillon Ngo e15170bc7f Merge branch 'dillon/46.9-warehouse-storage-charges-fixes' into 'master'
Multiple fixes related to storage invoices

See merge request CIEFWorldwideSdnBhd/shipping-portal!212
2024-03-26 00:44:33 +00:00
Dillon Ngo d7aa248972 Merge branch 'dillon/55-billplz-fix' into 'master'
Update info log location for vue polling related stuff + perfexcrm integration

See merge request CIEFWorldwideSdnBhd/shipping-portal!211
2024-03-18 12:17:11 +00:00
Dillon Ngo 8e4ff6093e Update info log location for VuePolling JobResourceNotFoundException and PerfexCRM 2024-03-18 14:06:30 +08:00
Dillon Ngo 584169e173 Update info log location for VuePolling JobResourceNotFoundException and PerfexCRM 2024-03-18 13:54:09 +08:00
Dillon Ngo 14278a888a Merge branch 'dillon/55-billplz-fix' into 'master'
Long term solution to prevent customer making payment to a shipping invoice...

See merge request CIEFWorldwideSdnBhd/shipping-portal!210
2024-03-16 05:52:58 +00:00
edmondlang 033a835fce update SendPermitsReminderEmails logging 2024-03-14 12:48:25 +08:00
edmondlang 66ddb5da23 update send permits reminder 2024-03-14 12:46:47 +08:00
Dillon Ngo 65782ebf8a Long term solution to prevent customer making payment to a shipping invoice when there is a ongoing dispute 2024-03-14 12:43:58 +08:00
Dillon Ngo 96a089df16 Long term solution to prevent customer making payment to a shipping invoice when there is a ongoing dispute 2024-03-14 12:32:03 +08:00
edmondlang f95545a985 update container remark in order page 2024-03-13 00:20:39 +08:00
edmondlang 9e8685bd02 update container remark in order page 2024-03-13 00:02:52 +08:00
edmondlang 1224735ba8 Merge branch 'master' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal 2024-03-12 23:52:22 +08:00
edmondlang 11e2390059 update container and container remarks 2024-03-12 23:52:07 +08:00
Dillon Ngo c280da36d7 Merge branch 'dillon/55-billplz-fix' into 'master'
Fix a problem reported by Yien where order shipping invoice pending payment despite payment made

See merge request CIEFWorldwideSdnBhd/shipping-portal!209
2024-03-12 06:32:13 +00:00
Dillon Ngo 27529e90ca Fix a problem reported by Yien where order shipping invoice pending payment despite payment made 2024-03-12 14:20:19 +08:00
Dillon Ngo 2e9b0f918c Merge branch 'dillon/46.8-warehouse-storage-charges-fixes' into 'master'
Change log location, 404 page

See merge request CIEFWorldwideSdnBhd/shipping-portal!208
2024-03-09 13:26:32 +00:00
Dillon Ngo 5565d0b39b Update log location JobResourceNotFoundException 2024-03-09 21:17:49 +08:00
Dillon Ngo 73b3995367 Merge branch 'dillon/46.9-warehouse-storage-charges-fixes' into 'master'
Temporary disable manual payment at order page for invoices with storage fee

See merge request CIEFWorldwideSdnBhd/shipping-portal!207
2024-03-08 13:12:33 +00:00
Dillon Ngo 252636a7df Fix 404 exception caught at production laravel logs 2024-03-08 08:46:05 +08:00
140 changed files with 3341 additions and 1075 deletions
+4
View File
@@ -59,3 +59,7 @@ YD_API_CODE=''
PERFEXCRM_BASE_URL=""
PERFEXCRM_API_KEY=""
PERFEXCRM_IS_ENABLED="false"
STORAGE_FEE_LAUNCH_DATE="2023-12-11 00:00:00"
SST_START_DATE="2024-04-01 00:00:00"
@@ -65,7 +65,7 @@ abstract class AbstractControllerLogic
} catch (ErrorException|GeneralExceptions|TypeError $exception){
if ($exception instanceof JobResourceNotFoundException) {
Log::error(sprintf(
Log::channel('vue_polling')->info(sprintf(
"Uncaught exception '%s' with message '%s' in %s:%d",
get_class($exception),
$exception->getMessage(),
@@ -0,0 +1,24 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class CompanySegmentsIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('companyModules', function ($module) use ($value) {
$module->whereHas('connections', function ($connection) use ($value) {
$connection->whereHas('connectionSegments', function ($segment) use ($value) {
$segment->whereIn('segment_id', $value);
});
});
});
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class MarkingIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('companyModules', function ($module) use ($value) {
$module->whereHas('connections', function ($connection) use ($value) {
$connection->whereIn('invitee_reference', $value);
});
});
}
}
@@ -13,6 +13,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class FetchOrdersFromYDPortalJob implements ShouldQueue
{
@@ -29,12 +30,17 @@ class FetchOrdersFromYDPortalJob implements ShouldQueue
*/
public function handle()
{
Log::info('FetchOrdersFromYDPortalJob starts');
Log::info('FetchPackingListsFromYdPortalProcessor starts');
(App()->make(FetchPackingListsFromYdPortalProcessor::class))->execute();
Log::info('FetchContainersFromYdPortalProcessor starts');
(App()->make(FetchContainersFromYdPortalProcessor::class))->execute();
Log::info('FetchContainersUpdatesFromYdPortalProcessor starts');
(App()->make(FetchContainersUpdatesFromYdPortalProcessor::class))->execute();
Log::info('FetchDeliveryUpdatesFromYdPortalProcessor starts');
(App()->make(FetchDeliveryUpdatesFromYdPortalProcessor::class))->execute();
// (App()->make(FetchOrderListsFromYdPortalProcessor::class))->execute();
Log::info('FetchOrdersFromYDPortalJob ends');
// (App()->make(FetchOrderListsFromYdPortalProcessor::class))->execute();
}
}
+2 -2
View File
@@ -47,7 +47,7 @@ class UpdatePerfexCRMInvoice implements ShouldQueue
$invoiceId = 0;
$invoiceStatus = 0;
$invoice = (App()->make(FetchesPerfexCRMInvoice::class))->execute($customer->userid,"INV-", $transaction->owner->bill_no);
Log::error('UpdatePerfexCRMInvoice debug bill_no: ' . $transaction->owner->bill_no . ', Project Id: ' . $this->updatePerfexCRMInvoiceObject->getProjectId());
Log::channel('perfex_crm')->info('UpdatePerfexCRMInvoice debug bill_no: ' . $transaction->owner->bill_no . ', Project Id: ' . $this->updatePerfexCRMInvoiceObject->getProjectId());
if(is_null($invoice)){
$result = (App()->make(CreatePerfexCRMInvoiceProcessor::class))->execute($transaction->owner, $this->updatePerfexCRMInvoiceObject->getIsPaid());
@@ -55,7 +55,7 @@ class UpdatePerfexCRMInvoice implements ShouldQueue
$invoiceId = $result->payload['id'];
} else {
// Log::error(json_encode('UpdatePerfexCRMInvoice CreatePerfexCRMInvoiceProcessor failed'));
Helper::debugLogger('UpdatePerfexCRMInvoice CreatePerfexCRMInvoiceProcessor failed');
Log::channel('perfex_crm')->info('UpdatePerfexCRMInvoice CreatePerfexCRMInvoiceProcessor failed');
}
}
else{
@@ -36,6 +36,8 @@ use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class CreateCustomerLogic extends AbstractControllerLogic
{
@@ -167,6 +169,19 @@ class CreateCustomerLogic extends AbstractControllerLogic
CreatePerfexCRMCustomer::dispatch($createLeadPerfexCRMObject);
}
if(app()->environment(['production'])){
// call wac webhook
$url = config('wagWebhookUrl.account_registration_url');
$payload = [
'name' => $request->input('name'),
'email' => $request->input('email'),
'phone' => $request->input('phone'),
'portal' => 'izyim'
];
$response = Http::post($url, $payload);
Log::channel('wac_webhook')->info('Register Account: ' . json_encode($response));
}
// $this->generateEmailVerificationAttemptProcessor->execute($user);
return $this->response($this->authenticationProcessor->execute($request));
@@ -0,0 +1,140 @@
<?php
namespace App\Classes\Modules\Billplzs\Processors;
use Illuminate\Http\Request;
use App\Models\Wallet;
use App\Models\Group;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\Modules\Transactions\Processors\CreatePaymentTransactionProcessor;
use App\Classes\Modules\Transactions\Processors\ReleaseGoodsToCustomerProcessor;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance;
use App\Classes\Modules\Orders\Processors\UpdateDoFromVTPortalProcessor;
use App\Classes\Modules\Orders\Processors\UpdateDoFromYDPortalProcessor;
use Illuminate\Support\Facades\Log;
//Date: 20240817
//This Processor is used for one time data patching with OneTimeTransactionFixBillplzFailedCallback.php
class CallbackBillplzDataPatchProcessor
{
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var UpdateDoFromVTPortalProcessor */
private $updateDoFromVTPortalProcessor;
/** @var UpdateDoFromYDPortalProcessor */
private $updateDoFromYDPortalProcessor;
/** @var UpdatesWalletBalance */
private $updatesWalletBalance;
/** @var CreatePaymentTransactionProcessor */
private $createPaymentTransactionProcessor;
/** @var ReleaseGoodsToCustomerProcessor */
private $releaseGoodsToCustomerProcessor;
/**
* CallbackBillplzDataPatchProcessor constructor.
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param UpdateDoFromVTPortalProcessor $updateDoFromVTPortalProcessor
* @param UpdateDoFromYDPortalProcessor $updateDoFromYDPortalProcessor
* @param UpdatesWalletBalance $updatesWalletBalance
* @param CreatePaymentTransactionProcessor $createPaymentTransactionProcessor
* @param ReleaseGoodsToCustomerProcessor $releaseGoodsToCustomerProcessor
*/
public function __construct(UpdatesTransactionStatus $updatesTransactionStatus, UpdateDoFromVTPortalProcessor $updateDoFromVTPortalProcessor, UpdateDoFromYDPortalProcessor $updateDoFromYDPortalProcessor, UpdatesWalletBalance $updatesWalletBalance, CreatePaymentTransactionProcessor $createPaymentTransactionProcessor, ReleaseGoodsToCustomerProcessor $releaseGoodsToCustomerProcessor)
{
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->updateDoFromVTPortalProcessor = $updateDoFromVTPortalProcessor;
$this->updateDoFromYDPortalProcessor = $updateDoFromYDPortalProcessor;
$this->updatesWalletBalance = $updatesWalletBalance;
$this->createPaymentTransactionProcessor = $createPaymentTransactionProcessor;
$this->releaseGoodsToCustomerProcessor = $releaseGoodsToCustomerProcessor;
}
/**
* @param Request $request
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute($transaction, $status)
{
$invoice = $transaction->owner;
$packingList = $invoice->owner;
$proceed = $this->checkForGroupPayment($transaction);
if(!$proceed) return false;
$this->updatesTransactionStatus->execute($transaction, $status);
Log::info('CallbackBillplzDataPatchProcessor 1');
// check if is wallet top up
if ($transaction->owner instanceof Wallet && $status === ApprovalStatus::APPROVED) {
Log::info('CallbackBillplzDataPatchProcessor 2');
$this->updatesWalletBalance->execute($transaction->owner, $transaction->amount);
$group = Group::where('reference', $transaction->payment_reference)->first();
// check if is group payment
if ($group) {
foreach ($group->groupTransactions as $groupTransaction) {
$invoice = $groupTransaction->transaction;
Log::info('CallbackBillplzDataPatchProcessor 3: '.json_encode($invoice));
//if($invoice->status !== ApprovalStatus::COMPLETED){
// $paymentTransaction = $this->createPaymentTransactionProcessor->execute($invoice, PaymentMethodType::WALLET, null, false);
// if($paymentTransaction && $paymentTransaction->status == ApprovalStatus::APPROVED){
$pL = $invoice->owner;
$this->releaseGoodsToCustomerProcessor->execute($pL, $invoice, true);
//}
//}
}
$group->status = $status;
$group->save();
}
}
if (!$transaction->owner instanceof Wallet) {
Log::info('CallbackBillplzDataPatchProcessor 3');
$this->releaseGoodsToCustomerProcessor->execute($packingList, $invoice);
}
return true;
}
private function checkForGroupPayment($transaction){
//This check is targetting group payment that expired and soft deleted
//command:check-storage-invoices must already run for this part of the code to work properly
$group = Group::withTrashed()->where('reference', $transaction->payment_reference)->first();
if ($group) {
$totalAmountToBePaid = 0;
$actualAmountPaid = $transaction->amount;
foreach ($group->groupTransactions as $groupTransaction) {
$invoice = $groupTransaction->transaction;
if($invoice->status !== ApprovalStatus::COMPLETED){
$totalAmountToBePaid += $invoice->amount;
}
}
if(($totalAmountToBePaid - $actualAmountPaid) < 0.01){
}
else{
Log::channel('storage_invoices')->info('Total amount from current transaction: '.$totalAmountToBePaid); //cief todo: to be removed
Log::channel('storage_invoices')->info('Total amount from paid transaction: '.$transaction->amount); //cief todo: to be removed
return false;
}
}
return true;
}
}
@@ -36,7 +36,7 @@ class CallbackBillplzProcessor
private $releaseGoodsToCustomerProcessor;
/**
* CreateUserProcessor constructor.
* CallbackBillplzProcessor constructor.
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param UpdateDoFromVTPortalProcessor $updateDoFromVTPortalProcessor
* @param UpdateDoFromYDPortalProcessor $updateDoFromYDPortalProcessor
@@ -7,6 +7,8 @@ use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Companies\Processors\AssignConnectionSegmentProcessor;
use App\Classes\Modules\Companies\Services\FetchesCompany;
use App\Classes\Modules\Companies\Services\FetchesCompanyConnection;
use App\Classes\Modules\Contacts\DataTransferObjects\ContactObject;
use App\Classes\Modules\Contacts\Processors\CreateContactProcessor;
use App\Http\Resources\CompanyResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -33,17 +35,22 @@ class AssignCompanyConnectionToConnectionSegmentLogic extends AbstractController
/** @var AssignConnectionSegmentProcessor */
private $assignCompanyConnectionToConnectionSegmentProcessor;
/** @var CreateContactProcessor */
private $createContactProcessor;
/**
* AssignCompanyToSegmentLogic constructor.
* @param FetchesCompany $fetchesCompany
* @param FetchesCompanyConnection $fetchesCompanyConnection
* @param AssignConnectionSegmentProcessor $assignCompanyConnectionToConnectionSegmentProcessor
* @param CreateContactProcessor $createContactProcessor
*/
public function __construct(FetchesCompany $fetchesCompany, FetchesCompanyConnection $fetchesCompanyConnection, AssignConnectionSegmentProcessor $assignCompanyConnectionToConnectionSegmentProcessor)
public function __construct(FetchesCompany $fetchesCompany, FetchesCompanyConnection $fetchesCompanyConnection, AssignConnectionSegmentProcessor $assignCompanyConnectionToConnectionSegmentProcessor, CreateContactProcessor $createContactProcessor)
{
$this->fetchesCompany = $fetchesCompany;
$this->fetchesCompanyConnection = $fetchesCompanyConnection;
$this->assignCompanyConnectionToConnectionSegmentProcessor = $assignCompanyConnectionToConnectionSegmentProcessor;
$this->createContactProcessor = $createContactProcessor;
}
/**
@@ -62,6 +69,11 @@ class AssignCompanyConnectionToConnectionSegmentLogic extends AbstractController
$this->assignCompanyConnectionToConnectionSegmentProcessor->execute($companyConnection, $request->input('segment_id'));
if ($request->input('segment_id') == 10 || $request->input('segment_id') == 11) {
$contactObject = new ContactObject('Whatsapp: ' . $request->input('name'), $request->input('phone'), null, null);
$this->createContactProcessor->execute($contactObject, $company);
}
return $this->resourceResponse(new CompanyResource($company));
}
}
@@ -24,8 +24,8 @@ class ExportsAgingList implements WithHeadings, WithHeadingRow, WithMapping, Sho
{
$this->filters = [
"has_invoice_status_in" => [2],
"packing_list_ordered_by_invoice_date" => true,
"with_aging_column" => true
// "packing_list_ordered_by_invoice_date" => true,
// "with_aging_column" => true
];
}
@@ -39,6 +39,7 @@ class ExportsAgingList implements WithHeadings, WithHeadingRow, WithMapping, Sho
'Invoice Date',
'Days',
'Amount',
'Credit Term Customer',
];
}
@@ -55,9 +56,16 @@ class ExportsAgingList implements WithHeadings, WithHeadingRow, WithMapping, Sho
$invDate = 'n/a';
$invNo = 'n/a';
$days = 'n/a';
$isCreditTermCustomer = false;
if ($list->owner instanceof Order) {
$inviterPivotInviteeReference = $list->owner->companyModule->inviters()->withPivot('invitee_reference')->first();
$companyModule = $list->owner->companyModule;
$segment = $companyModule->connections()->first()->segments()->where('name', 'Credit Term Customer')->first();
if ($segment) {
$isCreditTermCustomer = true;
}
if ($inviterPivotInviteeReference) {
$marking = $inviterPivotInviteeReference->pivot->invitee_reference;
@@ -87,6 +95,7 @@ class ExportsAgingList implements WithHeadings, WithHeadingRow, WithMapping, Sho
$invDate,
$days,
$amt,
$isCreditTermCustomer ? 'TRUE' : 'FALSE',
];
}
@@ -70,15 +70,31 @@ class ExportsCustomersWalletTransactionHistory implements FromQuery, WithHeading
$description = 'Credit Voucher for ' . $transaction->payment_reference;
break;
case TransactionType::PAYMENT:
$booking = Transaction::where('payment_reference', $transaction->bill_no)->first()->owner;
if (!$booking) {
$description = 'Payment for unknown booking, please contact tech support.';
$payment = Transaction::where('payment_reference', $transaction->bill_no)->first();
if (!$payment) {
$description = 'Payment not found, please contact tech support.';
break;
}
$marking = $booking->marking;
$description = 'Payment For booking refs' . $marking;
$invoice = $payment->owner;
if (!$invoice) {
$description = 'Invoice not found, please contact tech support.';
break;
}
$packingList = $invoice->owner;
if (!$packingList) {
$description = 'Packing List not found, please contact tech support.';
break;
}
$order = $packingList->owner;
if (!$order) {
$description = 'Order not found, please contact tech support.';
break;
}
$description = 'Payment For booking refs ' . $order->reference;
break;
case TransactionType::DEBIT_NOTE:
$description = 'Debit Voucher for ' . $transaction->payment_reference;
@@ -0,0 +1,108 @@
<?php
namespace App\Classes\Modules\Exports\Services;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\Group;
use App\Models\Transaction;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Illuminate\Http\Request;
use Carbon\Carbon;
class ExportsGroupTransactions implements
FromQuery,
WithHeadings,
WithHeadingRow,
WithMapping,
ShouldAutoSize
{
use Exportable;
private $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function headings(): array
{
return [
'DocNo',
'DocDate',
'PaymentDate',
'DebtorCode',
'Ref',
'ShipInfo',
'AccNo',
'DetailDescription',
'FurtherDescription',
'ProjNo',
'DeptNo',
'Qty',
'UnitPrice',
'TaxType',
'TaxableAmt',
'TaxRate'
];
}
public function query()
{
$start_date = $this->parseDate($this->request->input('startDate'));
$end_date = $this->parseDate($this->request->input('endDate'));
return Group::when($start_date && $end_date, function ($query) use ($start_date, $end_date) {
$query->whereBetween('created_at', [
$start_date->startOfDay(),
$end_date->endOfDay()
]);
})
->when($start_date && !$end_date, function ($query) use ($start_date) {
$query->where('created_at', '>=', $start_date->startOfDay());
})
->when(!$start_date && $end_date, function ($query) use ($end_date) {
$query->where('created_at', '<=', $end_date->endOfDay());
});
}
private function parseDate($date)
{
return $date ? Carbon::parse($date)->startOfDay() : null;
}
public function map($transaction): array
{
$company = $transaction->receiverCompany;
return [
'<<New>>', //'DocNo',
'', //'DocDate',
$transaction->created_at->format('m/d/Y H:m'), //'PaymentDate',
$company->debtor, //'DebtorCode',
$company->referece, //'Ref',
$company->referece, //'ShipInfo',
'500-0000', //'AccNo',
'X1 Freight Service Charge', //'DetailDescription',
'FurtherDescription', //'FurtherDescription',
'M3', //'ProjNo',
'CIEF', //'DeptNo',
'', //'Qty',
'', //'UnitPrice',
'', //'TaxType',
floatval($transaction->tax) > 0 ? number_format($transaction->tax, 2) : '0.00', //'TaxableAmt',
'', //'TaxRate'
];
}
private function getTaxType($tax_percentage)
{
return floatval($tax_percentage) > 0 ? 'SV-6' : '';
}
}
@@ -31,6 +31,7 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading
return [
'DocNo',
'DocDate',
'PaymentDate',
'DebtorCode',
'Ref',
'ShipInfo',
@@ -41,6 +42,9 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading
'DeptNo',
'Qty',
'UnitPrice',
'TaxType',
'TaxableAmt',
'TaxRate',
// 'marking',
// 'contact person',
// 'contact number',
@@ -56,12 +60,12 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading
{
$start_date = $this->request->input('startDate', null);
if ($start_date) {
$start_date = Carbon::parse($this->request->input('startDate'))->format('Y-m-d');
$start_date = Carbon::parse($start_date)->startOfDay();
}
$end_date = $this->request->input('endDate', null);
if ($end_date) {
$end_date = Carbon::parse($this->request->input('endDate'))->format('Y-m-d');
$end_date = Carbon::parse($end_date)->endOfDay();
}
$query = Transaction::query();
@@ -74,30 +78,31 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading
$approvalStatus = ApprovalStatus::APPROVED;
}
// $query->where('type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [$approvalStatus]);
$query->whereIn('type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->whereIn('status', [$approvalStatus]);
// Adjusted the query to include both types and status
$query->whereIn('type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])
->where('status', $approvalStatus);
if($start_date && $end_date) {
$query->whereBetween('updated_at', [
Carbon::parse($start_date)->format('Y-m-d 0:00:00'),
Carbon::parse($end_date)->format('Y-m-d 23:59:59')
]);
}
elseif($start_date && !$end_date) {
$query->whereHas('transactions', function($transaction) use ($start_date) {
$transaction->where('type', TransactionType::PAYMENT)->where('updated_at', '>=', Carbon::parse($start_date)->format('Y-m-d 0:00:00'));
if ($start_date && $end_date) {
$query->whereHas('transactions', function($transaction) use ($start_date, $end_date) {
$transaction->where('type', TransactionType::PAYMENT)
->whereBetween('updated_at', [$start_date, $end_date]);
});
}
elseif(!$start_date && $end_date) {
} elseif ($start_date) {
$query->whereHas('transactions', function($transaction) use ($start_date) {
$transaction->where('type', TransactionType::PAYMENT)
->where('updated_at', '>=', $start_date);
});
} elseif ($end_date) {
$query->whereHas('transactions', function($transaction) use ($end_date) {
$transaction->where('type', TransactionType::PAYMENT)->where('updated_at', '>=', Carbon::parse($end_date)->format('Y-m-d 0:00:00'));
$transaction->where('type', TransactionType::PAYMENT)
->where('updated_at', '<=', $end_date);
});
}
return $query;
}
public function map($transaction): array
{
$container = $transaction->owner->containers()->first();
@@ -130,9 +135,41 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading
$furtherDescription .= $item->name.' '.$item->quantity.' X '.$item->price."\n";
}
$transactionDetails = $transaction->transactionDetails;
$firstItem = true;
$rows = [];
foreach ($transactionDetails as $detail) {
// Prepare each row based on the transaction detail
$rows[] = [
$firstItem ? '<<New>>' : '',
$transaction->created_at->format('m/d/Y H:m'),
$transaction->transactions()->where('type', TransactionType::PAYMENT)->first()->updated_at->format('m/d/Y H:m'),
$company->debtor,
$order->reference,
$order->reference,
'500-0000',
str_replace('<br>', ' ', $detail->name),
'',
$container->reference,
'CIEF',
$detail->quantity,
$detail->price,
floatval($detail->tax_percentage) > 0 ? 'SV-6' : '',
floatval($detail->tax_percentage) > 0 ? number_format($detail->amount, 2) : '0.00',
$detail->tax_percentage,
];
if($firstItem) {
$firstItem = false;
}
}
return $rows;
return [
'<<New>>',
$transaction->created_at->format('m/d/Y H:m'),
$transaction->transactions()->where('type', TransactionType::PAYMENT)->first()->updated_at->format('m/d/Y H:m'),
$company->debtor,
$order->reference,
$order->reference,
@@ -56,27 +56,21 @@ class RescheduleContainerLogic extends AbstractControllerLogic
*/
public function logic(Request $request) : JsonResponse
{
try {
$container = $this->fetchesContainer->execute(['id' => $request->route('id')]);
$transport = $container->transports()->first();
$container = $this->fetchesContainer->execute(['id' => $request->route('id')]);
$transport = $container->transports()->first();
$old_sechedule = $transport->schedules()->first();
$old_schedule = $transport->schedules()->delete();
$this->updatesScheduleStatus->execute($old_sechedule, ApprovalStatus::REJECTED);
// $this->updatesScheduleStatus->execute($old_schedule, ApprovalStatus::REJECTED);
$scheduleObject = new ScheduleObject(
Carbon::parse($request->input('etd')),
Carbon::parse($request->input('eta')),
ApprovalStatus::APPROVED
);
$schedule = $this->createsSchedule->execute($transport, $scheduleObject);
return $this->resourceResponse(new ContainerResource($container));
} catch (\Exception $exception){
throw new ErrorException($exception->getMessage(), $exception->getCode());
}
$scheduleObject = new ScheduleObject(
Carbon::parse($request->input('etd')),
Carbon::parse($request->input('eta')),
ApprovalStatus::APPROVED
);
$schedule = $this->createsSchedule->execute($transport, $scheduleObject);
return $this->resourceResponse(new ContainerResource($container));
}
}
@@ -76,6 +76,7 @@ class FetchDeliveryUpdatesFromYdPortalProcessor
try {
$client = new \GuzzleHttp\Client(['cookies' => true, 'headers' => ['Cookie' => 'utc_offset=480']]);
Log::info('Delivery tracking sTrackingNo: '. $packingList->reference);
$request = $client->request('get', 'https://main.universe.com.my/Tracking/User/Paging?sEcho=1&sTrackingNo='.$packingList->reference.'&sOrgId=sti', ['timeout' => 3]);
$deliveryTracking = json_decode($request->getBody()->getContents());
foreach (array_reverse($deliveryTracking->aaData) as $trackingRow) {
@@ -119,7 +120,7 @@ class FetchDeliveryUpdatesFromYdPortalProcessor
}
}
Log::info('FetchDeliveryUpdatesFromYdPortalProcessor ends');
}
}
@@ -138,7 +138,13 @@ class FetchPackingListsFromYdPortalProcessor
$receiveDate = Carbon::parse(substr(preg_replace("/[^0-9]/", "", $row->expressno), 0, 8));
$customerno = preg_split('(-|\(|\)|\/)', $row->customerno);
$customerno = $row->customerno;
// Check if $customerno contains '正确唛头YD' and extract the part after it
if (strpos($customerno, '正确唛头YD') !== false) {
$customerno = explode('正确唛头YD', $customerno)[1];
}
$customerno = preg_split('/[-()\/]/', $customerno);
$orderNumber = $customerno[array_key_last($customerno)];
$allow_contract = true;
@@ -61,7 +61,7 @@ class FetchPerfexCRMInvoiceProcessor
$invoiceId = $result->payload['id'];
} else {
$log['message'] = 'FetchPerfexCRMInvoiceProcessor failed for transaction > bill_no: '.$number;
Helper::debugLogger($log);
Log::channel('perfex_crm')->info($log);
}
}
else{
@@ -25,7 +25,7 @@ class ConvertsPerfexCRMLeadToCustomer
return (object) $data;
}else{
Log::error('ConvertsPerfexCRMLeadToCustomer: '.$response);
Log::channel('perfex_crm')->info('ConvertsPerfexCRMLeadToCustomer: '.$response);
return null;
}
}
@@ -28,7 +28,7 @@ class CreatesPerfexCRMCustomer
$data = $response->json();
return (object) $data;
}else{
Log::error('CreatesPerfexCRMCustomer: '.$response);
Log::channel('perfex_crm')->info('CreatesPerfexCRMCustomer: '.$response);
return null;
}
}
@@ -35,7 +35,7 @@ class CreatesPerfexCRMCustomerContact
$data = $response->json();
return (object) $data;
}else{
Log::error('CreatesPerfexCRMCustomerContact: '.$response);
Log::channel('perfex_crm')->info('CreatesPerfexCRMCustomerContact: '.$response);
return null;
}
}
@@ -34,7 +34,7 @@ class CreatesPerfexCRMCustomerProject
$data = $response->json();
return (object) $data;
}else{
Log::error('CreatesPerfexCRMCustomerProject: '.$response);
Log::channel('perfex_crm')->info('CreatesPerfexCRMCustomerProject: '.$response);
return null;
}
}
@@ -54,7 +54,7 @@ class CreatesPerfexCRMInvoice
$data = $response->json();
return (object) $data;
}else{
Log::error('CreatesPerfexCRMInvoice: '.$response);
Log::channel('perfex_crm')->info('CreatesPerfexCRMInvoice: '.$response);
return null;
}
}
@@ -34,7 +34,7 @@ class CreatesPerfexCRMInvoicePayment
$data = $response->json();
return (object) $data;
}else{
Log::error('CreatesPerfexCRMInvoicePayment: '.$response);
Log::channel('perfex_crm')->info('CreatesPerfexCRMInvoicePayment: '.$response);
return null;
}
}
@@ -41,7 +41,7 @@ class CreatesPerfexCRMLead
$data = $response->json();
return (object) $data;
}else{
Log::error('CreatesPerfexCRMLead: '.$response);
Log::channel('perfex_crm')->info('CreatesPerfexCRMLead: '.$response);
return null;
}
}
@@ -37,7 +37,7 @@ class CreatesPerfexCRMMilestone
$data = $response->json();
return (object) $data;
}else{
Log::error('CreatesPerfexCRMMilestone: '.$response);
Log::channel('perfex_crm')->info('CreatesPerfexCRMMilestone: '.$response);
return null;
}
}
@@ -34,7 +34,7 @@ class CreatesPerfexCRMSupportTicket
$data = $response->json();
return (object) $data;
}else{
Log::error($response);
Log::channel('perfex_crm')->info($response);
return null;
}
}
@@ -58,7 +58,7 @@ class CreatesPerfexCRMTask
$data = $response->json();
return (object) $data;
}else{
Log::error('CreatesPerfexCRMTask: '.$response);
Log::channel('perfex_crm')->info('CreatesPerfexCRMTask: '.$response);
return null;
}
}
@@ -25,7 +25,7 @@ class FetchesPerfexCRMCustomer
return (object) $data;
}else{
Log::error('FetchesPerfexCRMCustomer: '.$response);
Log::channel('perfex_crm')->info('FetchesPerfexCRMCustomer: '.$response);
return null;
}
}
@@ -27,7 +27,7 @@ class FetchesPerfexCRMInvoice
return (object) $data;
}else{
Log::error('FetchesPerfexCRMInvoice: '.$response);
Log::channel('perfex_crm')->info('FetchesPerfexCRMInvoice: '.$response);
return null;
}
}
@@ -25,7 +25,7 @@ class FetchesPerfexCRMLead
return (object) $data;
}else{
Log::error('FetchesPerfexCRMLead: '.$response);
Log::channel('perfex_crm')->info('FetchesPerfexCRMLead: '.$response);
return null;
}
}
@@ -31,7 +31,7 @@ class FetchesPerfexCRMMilestone
return (object) $data;
}else{
Log::error('FetchesPerfexCRMMilestone: '.$response);
Log::channel('perfex_crm')->info('FetchesPerfexCRMMilestone: '.$response);
return null;
}
}
@@ -31,7 +31,7 @@ class FetchesPerfexCRMProject
return (object) $data;
}else{
Log::error('FetchesPerfexCRMProject: '.$response);
Log::channel('perfex_crm')->info('FetchesPerfexCRMProject: '.$response);
return null;
}
}
@@ -45,7 +45,7 @@ class FetchesPerfexCRMTask
return (object) $data;
}else{
Helper::debugLogger($response);
Log::channel('perfex_crm')->info($response);
return null;
}
}
@@ -39,7 +39,7 @@ class UpdatesPerfexCRMCustomer
$data = $response->json();
return (object) $data;
}else{
// Helper::debugLogger($response);
// Log::channel('perfex_crm')->info($response);
return null;
}
}
@@ -61,7 +61,7 @@ class UpdatesPerfexCRMInvoice
$data = $response->json();
return (object) $data;
}else{
Log::error('UpdatesPerfexCRMInvoice: '.$response);
Log::channel('perfex_crm')->info('UpdatesPerfexCRMInvoice: '.$response);
return null;
}
}
@@ -42,7 +42,7 @@ class UpdatesPerfexCRMLead
$data = $response->json();
return (object) $data;
}else{
Log::error('UpdatesPerfexCRMLead: '.$response);
Log::channel('perfex_crm')->info('UpdatesPerfexCRMLead: '.$response);
return null;
}
}
@@ -34,7 +34,7 @@ class UpdatesPerfexCRMProject
$data = $response->json();
return (object) $data;
}else{
Log::error('UpdatesPerfexCRMProject: '.$response);
Log::channel('perfex_crm')->info('UpdatesPerfexCRMProject: '.$response);
return null;
}
}
@@ -39,7 +39,7 @@ class UpdatesPerfexCRMTask
$data = $response->json();
return (object) $data;
}else{
Log::error('UpdatesPerfexCRMTask: '.$response);
Log::channel('perfex_crm')->info('UpdatesPerfexCRMTask: '.$response);
return null;
}
}
@@ -82,16 +82,35 @@ class CreateGroupsLogic extends AbstractControllerLogic
public function logic(Request $request) : JsonResponse
{
$payment_method = PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')];
$groupPyamentStatus = ApprovalStatus::PENDING_VERIFICATION;
$invoice_ids = json_decode($request->route('transaction_ids'));
$billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
// todo-new: check why is this not working
// $invoices = $this->fetchesTransaction->execute(['id_in' => $invoice_ids]);
$invoices = Transaction::whereIn('id', $invoice_ids)->get();
$isInvoicesApprove = $this->checkIfInvoicesAreApprove($invoices);
if(!$isInvoicesApprove){
$response = ['message' => 'One of the transactions might be suspended; please check if there are any disputes in progress.'];
return $this->response(['data' => $response]);
}
else{
$paymentMethodStr = $request->input('payment_method');
$paymentMethod = PaymentMethodType::PAYMENT_METHODS[$paymentMethodStr];
$amount = $request->input('amount');
$bankCode = $request->input('bank_code');
$result = $this->processInvoices($invoices, $paymentMethodStr, $amount, $bankCode);
if ($paymentMethod === PaymentMethodType::PAYMENT_GATEWAY) {
return $this->resourceResponse(new WalletTransactionResource($result));
}
}
return $this->response([]);
}
private function processInvoices($invoices, $reqPaymentMethod, $reqAmount, $reqBankCode){
$result = [];
$payment_method = PaymentMethodType::PAYMENT_METHODS[$reqPaymentMethod];
$groupPyamentStatus = ApprovalStatus::PENDING_VERIFICATION;
$billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
foreach ($invoices as $invoice) {
$order = null;
if ($invoice->owner instanceof Transaction) {
@@ -118,7 +137,7 @@ class CreateGroupsLogic extends AbstractControllerLogic
$wallet = Wallet::where('owner_id', $companyModuleId)->first();
// todo-new: verify currently checking wallet amount based on 'amount' value passed by frontend
if((float) number_format(($wallet->amount - $request->input('amount')),2) < 0){
if((float) number_format(($wallet->amount - $reqAmount),2) < 0){
throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.');
}
@@ -154,7 +173,7 @@ class CreateGroupsLogic extends AbstractControllerLogic
foreach ($invoices as $invoice) {
if ($payment_method == PaymentMethodType::WALLET) {
$paymentTransaction = $this->createPaymentTransactionProcessor->execute($invoice, PaymentMethodType::WALLET, $request->input('bank_code'), false);
$paymentTransaction = $this->createPaymentTransactionProcessor->execute($invoice, PaymentMethodType::WALLET, $reqBankCode, false);
if($paymentTransaction && $paymentTransaction->status == ApprovalStatus::APPROVED){
$pL = $invoice->owner;
@@ -181,11 +200,12 @@ class CreateGroupsLogic extends AbstractControllerLogic
if (in_array($payment_method, [PaymentMethodType::PAYMENT_GATEWAY, PaymentMethodType::CASH])) {
// create only one billplz payment for wallet top up
$amount = floatval(str_replace(',', '', $request->input('amount')));
$amount = floatval(str_replace(',', '', $reqAmount));
$companyModuleId = $invoice->receiver;
$companyModule = $this->fetchesCompanyModule->execute(['id' => $companyModuleId]);
$topUpTransaction = $this->createWalletTopUpTransactionProcessor->execute($companyModule, $amount, $request->input('bank_code'), $payment_method, $billNumber, true);
$topUpTransaction = $this->createWalletTopUpTransactionProcessor->execute($companyModule, $amount, $reqBankCode, $payment_method, $billNumber, true);
$result = $topUpTransaction;
}
$group->issuer = $issuer;
@@ -201,11 +221,15 @@ class CreateGroupsLogic extends AbstractControllerLogic
$group->status = $groupPyamentStatus;
$group->save();
if ($payment_method === PaymentMethodType::PAYMENT_GATEWAY) {
return $this->resourceResponse(new WalletTransactionResource($topUpTransaction));
return $result;
}
} else {
return $this->response([]);
private function checkIfInvoicesAreApprove($invoices){
foreach ($invoices as $invoice) {
if($invoice->status !== ApprovalStatus::APPROVED && $invoice->status !== ApprovalStatus::COMPLETED){
return false;
}
}
return true;
}
}
@@ -52,28 +52,18 @@ class CreatePaymentTransactionLogic extends AbstractControllerLogic
$invoice_transaction = $this->fetchesTransaction->execute(['id' => $request->input('transaction_id')]);
$payment_transaction = $this->createPaymentTransactionProcessor->execute($invoice_transaction, $payment_method , $request->input('bank_code'), false);
if($invoice_transaction->status === ApprovalStatus::APPROVED){
$payment_transaction = $this->createPaymentTransactionProcessor->execute($invoice_transaction, $payment_method , $request->input('bank_code'), false);
if($payment_transaction && $payment_transaction->status == ApprovalStatus::APPROVED){
$pL = $invoice_transaction->owner;
$this->releaseGoodsToCustomerProcessor->execute($pL, $invoice_transaction);
if($payment_transaction && $payment_transaction->status === ApprovalStatus::APPROVED){
$pL = $invoice_transaction->owner;
$this->releaseGoodsToCustomerProcessor->execute($pL, $invoice_transaction);
}
return $this->resourceResponse(new TransactionResource($payment_transaction));
}
//cief todo: at exchange there is a transition step - starts
// if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::PAYMENT_GATEWAY){
// $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::PENDING_VERIFICATION);
// }
// if(config('perfexcrm.is_enabled') == 'true'){
// $this->transactionToPerfexCRMV2Processor->execute($transaction, $status);
// }
//To use
//ApprovalStatus::PENDING_VERIFICATION;
//use this to trigger $this->transactionToPerfexCRMV2Processor->execute > defineTasks > defineTasks_handlePendingVerificationStatus > definePaymentTasks
//cief todo: at exchange there is a transition step - ends
return $this->resourceResponse(new TransactionResource($payment_transaction));
$response = ['message' => 'A transaction might be suspended; please check if there is any dispute in progress.'];
return $this->response(['data' => $response]);
}
@@ -48,6 +48,8 @@ class DeleteGroupLogic extends AbstractControllerLogic
*/
public function logic(Request $request) : JsonResponse
{
ini_set('memory_limit', '-1');
$group = $this->fetchesGroup->execute(['id' => $request->route('id')]);
foreach ($group->groupTransactions as $groupTransaction) {
@@ -0,0 +1,90 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\General\Eloquent\Filters\PaymentMethod;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\Modules\Transactions\Services\FetchesGroup;
use App\Classes\Modules\Transactions\Services\DeletesTransaction;
use App\Classes\Modules\Transactions\Services\DeletesGroup;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Transaction;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class DeletePaidGroupLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification(): array
{
return [
'title' => 'Delete Paid Group Transaction',
'message' => 'You have successfully deleted this Group Transaction'
];
}
/** @var FetchesGroup */
private $fetchesGroup;
/** @var DeletesGroup */
private $deletesGroup;
/**
* DeleteGroupLogic constructor.
* @param updatesTransactionStatus $updatesTransactionStatus
* @param FetchesGroup $fetchesGroup
* @param DeletesTransaction $deletesTransaction
*/
public function __construct(FetchesGroup $fetchesGroup, DeletesGroup $deletesGroup)
{
$this->fetchesGroup = $fetchesGroup;
$this->deletesGroup = $deletesGroup;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request): JsonResponse
{
$group = $this->fetchesGroup->execute(['id' => $request->route('id')]);
foreach ($group->groupTransactions as $groupTransaction) {
$invoice = $groupTransaction->transaction;
$paymentTransaction = $invoice->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->first();
if ($paymentTransaction->payment_method == PaymentMethodType::WALLET) {
// Store the transaction ID before deleting
$transactionId = Transaction::where('bill_no', $paymentTransaction->payment_reference)->first()->id;
// Delete the transaction
Transaction::where('id', $transactionId)->first()->delete();
// Now you have the transaction ID available in $transactionId
Log::channel('deletePaidGroupOrder')->info("Deleted payment reference transaction ID: " . $transactionId);
} else {
Log::channel('deletePaidGroupOrder')->info("no transaction found. Invoice Id ->" . $invoice->id);
}
// Store the transaction ID before deleting
$transactionId = $paymentTransaction->id;
// Delete the payment transaction
$paymentTransaction->delete();
Log::channel('deletePaidGroupOrder')->info("Deleted payment transaction ID: " . $transactionId);
}
Log::channel('deletePaidGroupOrder')->info("Deleted Group -> " . $group->id);
$this->deletesGroup->execute($group);
return $this->response([]);
}
}
@@ -15,6 +15,7 @@ use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Models\Document;
use Carbon\Carbon;
class RegenerateShippingInvoiceTransactionLogic extends AbstractControllerLogic
{
@@ -51,11 +52,18 @@ class RegenerateShippingInvoiceTransactionLogic extends AbstractControllerLogic
foreach ($orders as $order){
$invoices = $order->transactions()->where('transactions.type', TransactionType::SHIPPING_INVOICE)->get();
foreach ($invoices as $invoice){
$invoice->documents()->delete();
$transaction_invoice_pdf = LaravelMpdf::loadView('pages.pdfs.shipping_invoice', ['invoice_transaction' => $invoice]);
$view = 'pages.pdfs.shipping_invoice';
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
$shippingInvoiceTransactionCreatedDate = Carbon::parse($invoice->created_at);
if ($shippingInvoiceTransactionCreatedDate->isAfter($dateToCompare) && $invoice->tax > 0) {
$view = 'pages.pdfs.shipping_invoice_sst';
}
$transaction_invoice_pdf = LaravelMpdf::loadView($view, ['invoice_transaction' => $invoice]);
$document_object = new DocumentObject(
DocumentType::SHIPPING_INVOICE,
[chunk_split('data:application/pdf;base64,'.base64_encode($transaction_invoice_pdf->output()))],
@@ -63,11 +71,11 @@ class RegenerateShippingInvoiceTransactionLogic extends AbstractControllerLogic
ApprovalStatus::COMPLETED,
'shipping_invoice'
);
$document =$this->createsDocument->execute($invoice, $document_object);
$this->createsFiles->execute($document, $document_object);
// dump($document);
}
}
@@ -16,6 +16,7 @@ use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Models\Document;
use Carbon\Carbon;
class RegenerateSingleShippingInvoiceTransactionLogic extends AbstractControllerLogic
{
@@ -54,7 +55,14 @@ class RegenerateSingleShippingInvoiceTransactionLogic extends AbstractController
$invoice->documents()->delete();
$transaction_invoice_pdf = LaravelMpdf::loadView('pages.pdfs.shipping_invoice', ['invoice_transaction' => $invoice]);
$view = 'pages.pdfs.shipping_invoice';
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
$shippingInvoiceTransactionCreatedDate = Carbon::parse($invoice->created_at);
if ($shippingInvoiceTransactionCreatedDate->isAfter($dateToCompare) && $invoice->tax > 0) {
$view = 'pages.pdfs.shipping_invoice_sst';
}
$transaction_invoice_pdf = LaravelMpdf::loadView($view, ['invoice_transaction' => $invoice]);
$document_object = new DocumentObject(
DocumentType::SHIPPING_INVOICE,
@@ -25,6 +25,8 @@ use App\Classes\Modules\Transactions\Services\DeletesTransactionDetails;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionDetailObject;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\ValueObjects\Constants\TaxPercentage;
use Carbon\Carbon;
class UpdateShippingInvoiceTransactionLogic extends AbstractControllerLogic
{
@@ -60,9 +62,9 @@ class UpdateShippingInvoiceTransactionLogic extends AbstractControllerLogic
/** @var DeletesTransactionDetails */
private $deletesTransactionDetails;
public function __construct(
FetchesPackingList $fetchesPackingList,
FetchesPackingList $fetchesPackingList,
FetchesTransaction $fetchesTransaction,
UpdatesTransaction $updatesTransaction,
FetchesTransactionDetail $fetchesTransactionDetail,
@@ -105,11 +107,25 @@ class UpdateShippingInvoiceTransactionLogic extends AbstractControllerLogic
$transaction_detail = (object) $transaction_detail;
if(!in_array($transaction_detail->reference, ['SHIPPING_FEE', 'OVER_WEIGHT_CHARGES', 'MIN_CBM_CHARGES'])){
$reference = 'CUSTOM_CHARGES';
$taxPercentage = TaxPercentage::DEFAULT;
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
$shippingInvoiceTransactionCreatedDate = Carbon::parse($invoice_transaction->created_at);
if ($shippingInvoiceTransactionCreatedDate->isAfter($dateToCompare)) {
$taxPercentage = TaxPercentage::SIX_PERCENT;
}
if($transaction_detail->reference === "PAY_ON_BEHALF"){
$reference = "PAY_ON_BEHALF";
$taxPercentage = TaxPercentage::NO_TAX;
}
$object_detail = new TransactionDetailObject(
'CUSTOM_CHARGES',
$reference,
isset($transaction_detail->name) ? $transaction_detail->name : TransactionDetailType::CUSTOM_CHARGES,
$transaction_detail->quantity,
$transaction_detail->price
$transaction_detail->price,
$taxPercentage
);
if(isset($transaction_detail->id)){
@@ -121,20 +137,20 @@ class UpdateShippingInvoiceTransactionLogic extends AbstractControllerLogic
}
$object = new TransactionObject(
$invoice_transaction->bill_no,
TransactionType::SHIPPING_INVOICE,
1,
$invoice_transaction->bill_no,
TransactionType::SHIPPING_INVOICE,
1,
$invoice_transaction->issuer,
1,
1,
PaymentMethodType::CASH,
$invoice_transaction->transactionDetails()->sum('amount'),
$invoice_transaction->transactionDetails()->sum('amount'),
1,
1,
0,
0,
1,
0,
null,
$invoice_transaction->transactionDetails()->sum('tax_amount'),
0,
null,
ApprovalStatus::PENDING_VERIFICATION
);
@@ -18,19 +18,25 @@ class TransactionDetailObject implements DataTransferObject
/** @var float|null */
private $price;
/** @var float|null */
private $taxPercentage;
/**
* TransactionDetailObject constructor.
* @param string $reference
* @param string $name
* @param float $quantity
* @param float $price
* @param float $taxPercentage
*/
public function __construct(?string $reference, ?string $name , ?float $quantity, ?float $price)
public function __construct(?string $reference, ?string $name , ?float $quantity, ?float $price, ?float $taxPercentage)
{
$this->reference = $reference;
$this->name = $name;
$this->quantity = $quantity;
$this->price = $price;
$this->taxPercentage = $taxPercentage;
}
/**
@@ -65,9 +71,28 @@ class TransactionDetailObject implements DataTransferObject
return $this->price;
}
/**
* @return float
*/
public function getAmount(): float
{
return $this->getQuantity() * $this->getPrice();
return ($this->getQuantity() * $this->getPrice()) + $this->getTaxAmount();
}
/**
* @return float
*/
public function getTaxPercentage(): ?float
{
return $this->taxPercentage;
}
/**
* @return float
*/
public function getTaxAmount(): float
{
return ($this->getQuantity() * $this->getPrice()) * $this->getTaxPercentage() / 100;
}
}
@@ -80,21 +80,21 @@ class TransactionObject implements DataTransferObject
* @param array|null $details
*/
public function __construct(
string $billNo,
string $transactionType,
int $issuer,
int $receiver,
int $recipientBankAccountId,
int $paymentMethod,
float $amount,
float $originalAmount,
string $billNo,
string $transactionType,
int $issuer,
int $receiver,
int $recipientBankAccountId,
int $paymentMethod,
float $amount,
float $originalAmount,
int $currencyId,
int $originalCurrencyId,
int $originalCurrencyId,
float $currencyRate,
float $tax,
float $serviceCharge,
?Carbon $expiresOn,
?int $status = ApprovalStatus::PENDING_SUBMISSION,
float $tax,
float $serviceCharge,
?Carbon $expiresOn,
?int $status = ApprovalStatus::PENDING_SUBMISSION,
?array $details = [],
?string $paymentReference = null
)
@@ -241,10 +241,10 @@ class TransactionObject implements DataTransferObject
/**
* @return array
*/
public function getDetails(): array
public function getDetails(): array //cief todo: not in used as of 20240407
{
return array_map(function($product){
return new TransactionDetailObject($product['stockCode'] ?? '', $product['description'] ?? '', $product['quantity'] ?? '', floatval(str_replace(',', '', $product['unit_price'])));
return new TransactionDetailObject($product['stockCode'] ?? '', $product['description'] ?? '', $product['quantity'] ?? '', floatval(str_replace(',', '', $product['unit_price'])), 0.0 );
}, $this->details);
}
@@ -255,4 +255,4 @@ class TransactionObject implements DataTransferObject
{
return $this->paymentReference;
}
}
}
@@ -19,6 +19,7 @@ use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\Jobs\CreatePerfexCRMInvoice;
use App\Classes\Modules\Transactions\Processors\CheckStorageInvoiceTransactionProcessor;
use App\Models\Order;
use Carbon\Carbon;
class ApproveShippingInvoiceTransactionProcessor
{
@@ -59,9 +60,16 @@ class ApproveShippingInvoiceTransactionProcessor
/** @var Transaction $invoice_transaction */
$invoice_transaction = $packingList->transactions()->where('transactions.type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::PENDING_SUBMISSION])->first();
$view = 'pages.pdfs.shipping_invoice';
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
$shippingInvoiceTransactionCreatedDate = Carbon::parse($invoice_transaction->created_at);
if ($shippingInvoiceTransactionCreatedDate->isAfter($dateToCompare) && $invoice_transaction->tax > 0) {
$view = 'pages.pdfs.shipping_invoice_sst';
}
$this->updatesTransactionStatus->execute($invoice_transaction, ApprovalStatus::APPROVED);
$transaction_invoice_pdf = LaravelMpdf::loadView('pages.pdfs.shipping_invoice', ['invoice_transaction' => $invoice_transaction]);
$transaction_invoice_pdf = LaravelMpdf::loadView($view, ['invoice_transaction' => $invoice_transaction]);
$document_object = new DocumentObject(
DocumentType::SHIPPING_INVOICE,
@@ -23,6 +23,7 @@ use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PackageType;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TaxPercentage;
use App\Classes\ValueObjects\Constants\TransactionDetailType;
use App\Http\Resources\TransactionWithStorageResource;
use App\Models\Order;
@@ -163,6 +164,7 @@ class CheckStorageInvoiceTransactionProcessor
if ($transport) {
$schedule = $transport->schedules->last();
if ($schedule) {
Log::channel('storage_invoices')->info('schedule: '.json_encode($schedule));
return $schedule->eta;
}
}
@@ -176,11 +178,14 @@ class CheckStorageInvoiceTransactionProcessor
$pricePerCBM = 3;
$resultNumberOfDaysFree = 10;
$dt1 = $eta->copy()->addDay()->startOfDay();
$resultStartDate = $dt1->format('Y-m-d');
$currentDatetime = Carbon::now();
$dt2 = $currentDatetime->copy()->addDay()->startOfDay();
$resultCurrentDate = $dt2->format('Y-m-d H:i:s');
$dt2 = Carbon::now()->copy()->addDay()->startOfDay();
$interval = Carbon::parse($dt2)->diff($dt1);
Log::channel('storage_invoices')->info('eta: '.json_encode($eta));
Log::channel('storage_invoices')->info('dt1: '.json_encode($dt1));
Log::channel('storage_invoices')->info('dt2: '.json_encode($dt2));
Log::channel('storage_invoices')->info('interval: '.json_encode($interval));
Log::channel('storage_invoices')->info('Carbon now: '.json_encode(Carbon::now()));
$resultNumberOfDaysExceeded = $interval->days - $resultNumberOfDaysFree;
$storageInvoice = $destinationWarehousePackage->transactions()->where('transactions.type', TransactionType::STORAGE_INVOICE)->first();
@@ -195,14 +200,25 @@ class CheckStorageInvoiceTransactionProcessor
$cbm = $cbm + $quantity;
}
$total_tax = 0;
$taxPercentage = TaxPercentage::DEFAULT;
$price_cbm = $pricePerCBM * $cbm * $resultNumberOfDaysExceeded;
Log::channel('storage_invoices')->info('storageInvoice: '.json_encode($storageInvoice).', $transaction->status: '.$transaction->status);
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
Log::channel('storage_invoices')->info('dateToCompare: '.json_encode($dateToCompare));
if(!$storageInvoice && $resultNumberOfDaysExceeded > 0 && $transaction->status != ApprovalStatus::COMPLETED){
Log::channel('storage_invoices')->info('Created $transaction->id: '.$transaction->id);
$billNumber = $this->generatesTransactionBillNumber->execute('STOR-');
$storageInvoice = $this->createStorageInvoiceTransaction($destinationWarehousePackage, $billNumber, $company_module_id, $price_cbm);
if (Carbon::now()->isAfter($dateToCompare)) {
$taxPercentage = TaxPercentage::SIX_PERCENT;
$total_tax = $price_cbm * $taxPercentage / 100;
$price_cbm = $price_cbm + $total_tax;
}
$storageInvoice = $this->createStorageInvoiceTransaction($destinationWarehousePackage, $billNumber, $company_module_id, $price_cbm, $total_tax);
$storageInvoiceId = $storageInvoice->id;
$this->createStorageInvoiceTransactionDetails($storageInvoice, $destinationWarehousePackage, $cbm, $pricePerCBM, $resultNumberOfDaysExceeded);
$this->createStorageInvoiceTransactionDetails($storageInvoice, $destinationWarehousePackage, $cbm, $pricePerCBM, $resultNumberOfDaysExceeded, $taxPercentage);
}
else if($storageInvoice){
//Additional handling for giving selected storage invoice a waiver
@@ -215,12 +231,18 @@ class CheckStorageInvoiceTransactionProcessor
$paymentStorageTransaction = $storageInvoice->transactions()->where('transactions.type', TransactionType::PAYMENT)->where('transactions.status', ApprovalStatus::APPROVED)->first();
if($paymentStorageTransaction){
$dateStorageInvoicePaid = $paymentStorageTransaction->created_at->copy()->addDay()->startOfDay();
Log::channel('storage_invoices')->info('dateStorageInvoicePaid: '.$dateStorageInvoicePaid.', resultCurrentDate: '.$resultCurrentDate);
Log::channel('storage_invoices')->info('dateStorageInvoicePaid: '.$dateStorageInvoicePaid.', resultCurrentDate: '.$dt2->format('Y-m-d H:i:s'));
$intervalRecalculate = Carbon::parse($dateStorageInvoicePaid)->diff($dt1);
$resultNumberOfDaysExceeded = $intervalRecalculate->days - $resultNumberOfDaysFree;
$price_cbm = $pricePerCBM * $cbm * $resultNumberOfDaysExceeded;
}
if (Carbon::now()->isAfter($dateToCompare)) {
$taxPercentage = TaxPercentage::SIX_PERCENT;
$total_tax = $price_cbm * $taxPercentage / 100;
$price_cbm = $price_cbm + $total_tax;
}
$amount = $storageInvoice->amount;
$epsilon = 0.0001; // Tolerance for the comparison
Log::channel('storage_invoices')->info('Update $transaction->id: '.$transaction->id);
@@ -246,9 +268,10 @@ class CheckStorageInvoiceTransactionProcessor
if($proceedToUpdate || ($pricePerCBM == 0 && $amount != 0)){
Log::channel('storage_invoices')->info('proceedToUpdate');
$storageInvoice = $this->updateStorageInvoiceTransaction($storageInvoice, $price_cbm);
$storageInvoice = $this->updateStorageInvoiceTransaction($storageInvoice, $price_cbm, $total_tax);
$invoiceTransactionDetails = $storageInvoice->transactionDetails()->first();
$this->updateStorageInvoiceTransactionDetails($invoiceTransactionDetails, $destinationWarehousePackage, $cbm, $pricePerCBM, $resultNumberOfDaysExceeded);
$this->updateStorageInvoiceTransactionDetails($invoiceTransactionDetails, $destinationWarehousePackage, $cbm, $pricePerCBM, $resultNumberOfDaysExceeded, $taxPercentage);
}
$storageInvoiceId = $storageInvoice->id;
@@ -260,8 +283,8 @@ class CheckStorageInvoiceTransactionProcessor
'storageInvoiceId' => $storageInvoiceId,
'numberOfDaysExceeded' => $resultNumberOfDaysExceeded,
'numberOfDaysFree' => $resultNumberOfDaysFree,
'startDate' => $resultStartDate,
'currentDate' => $resultCurrentDate,
'startDate' => $dt1->format('Y-m-d'),
'currentDate' => $dt2->format('Y-m-d H:i:s'),
'cbm' => $cbm,
'pricePerCBM' => $pricePerCBM,
'storageInvoice' => new TransactionWithStorageResource($storageInvoice)
@@ -356,7 +379,7 @@ class CheckStorageInvoiceTransactionProcessor
}
}
private function updateStorageInvoiceTransaction(Transaction $transaction, float $totalAmount){
private function updateStorageInvoiceTransaction(Transaction $transaction, float $totalAmount, float $totalTax){
$object = new TransactionObject(
$transaction->bill_no,
@@ -370,7 +393,7 @@ class CheckStorageInvoiceTransactionProcessor
1,
1,
0,
0,
$totalTax,
0,
null,
ApprovalStatus::APPROVED
@@ -382,18 +405,19 @@ class CheckStorageInvoiceTransactionProcessor
return $invoice_transaction;
}
private function updateStorageInvoiceTransactionDetails($invoice_transaction_details, PackingList $packing_list, $cbm, $pricePerCBM, $numberOfDays){
private function updateStorageInvoiceTransactionDetails($invoice_transaction_details, PackingList $packing_list, $cbm, $pricePerCBM, $numberOfDays, $taxPercentage){
$object_detail = new TransactionDetailObject(
'STORAGE_FEE',
TransactionDetailType::STORAGE_FEE.' for '.$numberOfDays. ' days * RM'.$pricePerCBM.'<br>'.round($packing_list->packages->where('type', '!=', PackageType::OVER_WEIGHT)->sum('quantity'), 3).' CTNS - '.round($cbm, 3).' CBM',
$cbm,
$pricePerCBM * $numberOfDays
$pricePerCBM * $numberOfDays,
$taxPercentage
);
$this->updatesTransactionDetail->execute($invoice_transaction_details, $object_detail);
}
private function createStorageInvoiceTransaction(PackingList $packing_list, string $billNumber, int $companyModuleId, float $totalAmount){
private function createStorageInvoiceTransaction(PackingList $packing_list, string $billNumber, int $companyModuleId, float $totalAmount, float $totalTax){
$object = new TransactionObject(
$billNumber,
@@ -407,7 +431,7 @@ class CheckStorageInvoiceTransactionProcessor
1,
1,
0,
0,
$totalTax,
0,
null,
ApprovalStatus::APPROVED
@@ -419,12 +443,13 @@ class CheckStorageInvoiceTransactionProcessor
return $invoice_transaction;
}
private function createStorageInvoiceTransactionDetails($invoice_transaction, PackingList $packing_list, $cbm, $pricePerCBM, $numberOfDays){
private function createStorageInvoiceTransactionDetails($invoice_transaction, PackingList $packing_list, $cbm, $pricePerCBM, $numberOfDays, $taxPercentage){
$object_detail = new TransactionDetailObject(
'STORAGE_FEE',
TransactionDetailType::STORAGE_FEE.' for '.$numberOfDays. ' days * RM'.$pricePerCBM.'<br>'.round($packing_list->packages->where('type', '!=', PackageType::OVER_WEIGHT)->sum('quantity'), 3).' CTNS - '.round($cbm, 3).' CBM',
$cbm,
$pricePerCBM * $numberOfDays
$pricePerCBM * $numberOfDays,
$taxPercentage
);
$this->createsTransactionDetail->execute($invoice_transaction, $object_detail);
@@ -22,15 +22,19 @@ use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PackageType;
use App\Classes\ValueObjects\Constants\SegmentConstants;
use App\Classes\ValueObjects\Constants\TaxPercentage;
use App\Classes\ValueObjects\Constants\TransactionDetailType;
use App\Models\CompanyModule;
use App\Models\Document;
use App\Models\PackingList;
use App\Models\Transaction;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use Illuminate\Support\Facades\Http;
class CreateInvoiceTransactionProcessor
{
@@ -71,6 +75,18 @@ class CreateInvoiceTransactionProcessor
*/
public function execute(PackingList $packingList)
{
$ori_packing_list = $packingList;
// call wac webhook
$hasInvoiceCreated = Transaction::withTrashed()
->where('type', TransactionType::SHIPPING_INVOICE)
->where('owner_type', PackingList::class)
->where('owner_id', $ori_packing_list->id)
->exists();
if ($hasInvoiceCreated) {
Log::channel('wac_webhook')->info('Regenerating Invoice for PackingList: ' . $ori_packing_list->id . '. No call wac api.');
}
$packing_list = PackingList::where('reference', $packingList->reference)->where('type', PackingListType::SHIPPING_PACKING_LIST)->first();
$billable_packing_list = $packing_list->packingLists()->first();
@@ -171,6 +187,16 @@ class CreateInvoiceTransactionProcessor
$billNumber = $this->generatesTransactionBillNumber->execute('SHIP-');
$total_tax = 0;
$taxPercentage = TaxPercentage::DEFAULT;
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
$shippingInvoiceTransactionCreatedDate = Carbon::now();
if ($shippingInvoiceTransactionCreatedDate->isAfter($dateToCompare)) {
$taxPercentage = TaxPercentage::SIX_PERCENT;
$total_tax = $total_cbm * $taxPercentage / 100;
$total_cbm = $total_cbm + $total_tax;
}
$object = new TransactionObject(
$billNumber,
TransactionType::SHIPPING_INVOICE,
@@ -183,7 +209,7 @@ class CreateInvoiceTransactionProcessor
1,
1,
0,
0,
$total_tax,
0,
null,
ApprovalStatus::PENDING_SUBMISSION
@@ -196,7 +222,8 @@ class CreateInvoiceTransactionProcessor
'SHIPPING_FEE',
TransactionDetailType::SHIPPING_FEE.'<br>'.round($packing_list->packages->where('type', '!=', PackageType::OVER_WEIGHT)->sum('quantity'), 3).' CTNS - '.round($cbm, 3).' CBM',
$cbm,
$price_cbm
$price_cbm,
$taxPercentage
);
$this->createsTransactionDetail->execute($invoice_transaction, $object_detail);
@@ -206,7 +233,8 @@ class CreateInvoiceTransactionProcessor
'OVER_WEIGHT_CHARGES',
TransactionDetailType::OVER_WEIGHT_CHARGES,
$over_weight_cbm,
$price_cbm
$price_cbm,
$taxPercentage
);
$this->createsTransactionDetail->execute($invoice_transaction, $object_detail);
@@ -217,11 +245,38 @@ class CreateInvoiceTransactionProcessor
'MIN_CBM_CHARGES',
TransactionDetailType::MIN_CBM_CHARGES,
$minimum_charge,
$price_cbm
$price_cbm,
$taxPercentage
);
$this->createsTransactionDetail->execute($invoice_transaction, $object_detail);
}
if (app()->environment('production') && !$hasInvoiceCreated) {
$order = $ori_packing_list->owner;
$companyModule = $order->companyModule;
$company = $companyModule->company;
$companyContact = $company->contacts()->first();
$employee = $companyModule->employees()->first();
$invoiceCount = Transaction::where('type', TransactionType::SHIPPING_INVOICE)
->where('receiver', $companyModule->id)
->where('status', ApprovalStatus::APPROVED)
->count();
$payload = [
'name' => $employee->name,
'email' => $employee->email,
'phone' => $companyContact->phone,
'order_reference' => $order->reference,
'portal' => 'izyim',
'invoice_count' => $invoiceCount,
];
Log::channel('wac_webhook')->info('Attempt to send wac_webhook - Invoice Created: ' . json_encode($payload));
$response = Http::post(config('wagWebhookUrl.invoice_created_url'), $payload);
Log::channel('wac_webhook')->info('Invoice Created: ' . json_encode($response));
}
return;
}
@@ -0,0 +1,188 @@
<?php
namespace App\Classes\Modules\Transactions\Processors;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Transactions\Services\CreatesPaymentTransaction;
use App\Classes\Modules\Transactions\Services\CreatesTransactionDetail;
use App\Classes\Modules\Billplzs\Services\CreatesBillplzBill;
use App\Classes\Modules\Transactions\Services\CreatesTransactionableTransaction;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance;
use App\Models\Transaction;
use App\Models\Wallet;
use App\Classes\Modules\Orders\Processors\UpdateDoFromVTPortalProcessor;
use App\Classes\Modules\Orders\Processors\UpdateDoFromYDPortalProcessor;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use Illuminate\Support\Facades\Log;
//DATE: 20240616
//THIS IS A ONE TIME FIX PROCESSOR MEANT TO FIX A GROUP PAYMENT THAT GOT STUCK: https://izyim.cief-malaysia.com/customer/943GCC/payment-and-billing
//TRNASACTION WITH ID: 16803
//GROUP WITH ID: 609
class CreatePaymentTransactionOneTimeFixProcessor
{
/** @var GeneratesTransactionBillNumber */
private $generatesTransactionBillNumber;
/** @var CreatesPaymentTransaction */
private $createsPaymentTransaction;
/** @var CreatesBillplzBill */
private $createsBillplzBill;
/** @var CreatesTransactionableTransaction */
private $createsTransactionableTransaction;
/** @var UpdatesWalletBalance */
private $updatesWalletBalance;
/** @var UpdateDoFromVTPortalProcessor */
private $updateDoFromVTPortalProcessor;
/** @var UpdateDoFromYDPortalProcessor */
private $updateDoFromYDPortalProcessor ;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus ;
/**
* @param FetchesTransaction $fetchesTransaction,
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber,
* @param CreatesPaymentTransaction $createsPaymentTransaction
* @param CreatesTransactionDetail $createsTransactionDetail
* @param CreatesBillplzBill $createsBillplzBil
* @param CreatesTransactionableTransaction $createsTransactionableTransaction
* @param UpdatesWalletBalance $updatesWalletBalance
* @param UpdateDoFromVTPortalProcessor $updateDoFromVTPortalProcessor
* @param UpdateDoFromYDPortalProcessor $updateDoFromYDPortalProcessor
* @param UpdatesTransactionStatus $updatesTransactionStatus
*/
public function __construct(
GeneratesTransactionBillNumber $generatesTransactionBillNumber,
CreatesPaymentTransaction $createsPaymentTransaction,
CreatesBillplzBill $createsBillplzBill,
CreatesTransactionableTransaction $createsTransactionableTransaction,
UpdatesWalletBalance $updatesWalletBalance,
UpdateDoFromVTPortalProcessor $updateDoFromVTPortalProcessor,
UpdateDoFromYDPortalProcessor $updateDoFromYDPortalProcessor,
UpdatesTransactionStatus $updatesTransactionStatus
)
{
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createsPaymentTransaction = $createsPaymentTransaction;
$this->createsBillplzBill = $createsBillplzBill;
$this->createsTransactionableTransaction = $createsTransactionableTransaction;
$this->updatesWalletBalance = $updatesWalletBalance;
$this->updateDoFromVTPortalProcessor = $updateDoFromVTPortalProcessor;
$this->updateDoFromYDPortalProcessor = $updateDoFromYDPortalProcessor;
$this->updatesTransactionStatus = $updatesTransactionStatus;
}
/**
* @throws MalformedRequestException
*/
public function execute(Transaction $invoice, $payment_method, $bank_code, $date, $run = true)
{
$amount = $invoice->amount;
Log::info($invoice->owner);
$company_module = $invoice->owner->owner->companyModule()->first();
$approvalStatus = ApprovalStatus::PENDING_SUBMISSION;
$billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
$payment_reference = null;
if ($payment_method == PaymentMethodType::PAYMENT_GATEWAY) {
// create billplz transaction
$payment_method = PaymentMethodType::PAYMENT_GATEWAY;
$billPlzBill = $this->createsBillplzBill->execute(
$company_module->name,
(app()->environment(['production'])) ? $company_module->employees()->first()->email : 'uldvstar@gmail.com',
'This payment is for the invoice number . ' . $billNumber,
$amount,
$billNumber,
$bank_code,
true
);
$payment_reference = $billPlzBill->id;
}
else if ($payment_method === PaymentMethodType::WALLET) {
/** @var Wallet $wallet */
$wallet = $company_module->wallets()->first();
// if((float) number_format(($wallet->amount - $amount),2) < 0){
// throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.');
// }
$walletPaymentBillNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
$transaction_object = new TransactionObject($walletPaymentBillNumber, TransactionType::PAYMENT, 1, $company_module->id, 1, PaymentMethodType::WALLET, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], '');
$transaction = $this->createsTransactionableTransaction->execute($wallet, $transaction_object);
$transaction->created_at = $date;
$transaction->updated_at = $date;
$transaction->save();
$payment_reference = $walletPaymentBillNumber;
$this->updatesWalletBalance->execute($wallet, ($amount * -1));
if($run)
{
$packingList = $invoice->owner;
$order = $packingList->owner;
$packingList->status = ApprovalStatus::APPROVED;
$packingList->save();
if(app()->environment('production')){
$this->updateDoFromVTPortalProcessor->execute($packingList);
$this->updateDoFromYDPortalProcessor->execute($packingList);
}
}
// later use this variabke to create a approved payment transaction
$approvalStatus = ApprovalStatus::APPROVED;
// update invoice to completed
if($run){
$this->updatesTransactionStatus->execute($invoice, ApprovalStatus::COMPLETED);
}
}
else {
$payment_method = PaymentMethodType::CASH;
}
$object = new TransactionObject(
$billNumber,
TransactionType::PAYMENT,
$company_module->id,
1,
1,
$payment_method,
$amount,
$amount,
1,
1,
0,
0,
0,
null,
$approvalStatus,
null,
$payment_reference
);
$payment_transaction = $this->createsPaymentTransaction->execute($invoice, $object);
return $payment_transaction;
}
}
@@ -114,7 +114,7 @@ class CreatePaymentTransactionProcessor
/** @var Wallet $wallet */
$wallet = $company_module->wallets()->first();
if((float) number_format(($wallet->amount - $amount),2) < 0){
if((float) number_format(($wallet->amount - $amount),2) < -0.01){
throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.');
}
@@ -9,6 +9,7 @@ use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\SegmentConstants;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TaxPercentage;
use App\Models\Order;
class CreateShippingCostTransactionProcessor
@@ -57,7 +58,8 @@ class CreateShippingCostTransactionProcessor
TransactionType::SHIPPING_COST,
'Shipping Cost',
$quantity,
$totalShippingCost/$totalCBM
$totalShippingCost/$totalCBM,
TaxPercentage::SIX_PERCENT
);
$shippingCostTransaction = $this->createsShippingCostTransaction->execute($order, $transactionObject, $transactionDetailObject);
@@ -16,6 +16,8 @@ use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class CreateStorageInvoiceDocTransactionFixProcessor
{
@@ -51,9 +53,17 @@ class CreateStorageInvoiceDocTransactionFixProcessor
/** @var Transaction $invoice_transaction */
$invoice_transaction = $packingList->transactions()->where('transactions.type', TransactionType::STORAGE_INVOICE)->whereIn('status', [ApprovalStatus::COMPLETED])->first();
// $this->updatesTransactionStatus->execute($invoice_transaction, ApprovalStatus::APPROVED);
// $this->updatesTransactionStatus->execute($invoice_transaction, ApprovalStatus::APPROVED);
$view = 'pages.pdfs.shipping_invoice';
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
Log::info('$dateToCompare 2: '.$dateToCompare);
$storageInvoiceTransactionCreatedDate = Carbon::parse($invoice_transaction->created_at);
Log::info('$storageInvoiceTransactionCreatedDate 2: '.$storageInvoiceTransactionCreatedDate);
if ($storageInvoiceTransactionCreatedDate->isAfter($dateToCompare) && $invoice_transaction->tax > 0) {
$view = 'pages.pdfs.shipping_invoice_sst';
}
$transaction_invoice_pdf = LaravelMpdf::loadView('pages.pdfs.shipping_invoice', ['invoice_transaction' => $invoice_transaction]);
$transaction_invoice_pdf = LaravelMpdf::loadView($view, ['invoice_transaction' => $invoice_transaction]);
$document_object = new DocumentObject(
DocumentType::STORAGE_INVOICE,
@@ -16,6 +16,8 @@ use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class CreateStorageInvoiceDocTransactionProcessor
{
@@ -53,7 +55,16 @@ class CreateStorageInvoiceDocTransactionProcessor
// $this->updatesTransactionStatus->execute($invoice_transaction, ApprovalStatus::APPROVED);
$transaction_invoice_pdf = LaravelMpdf::loadView('pages.pdfs.shipping_invoice', ['invoice_transaction' => $invoice_transaction]);
$view = 'pages.pdfs.shipping_invoice';
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
Log::info('$dateToCompare: '.$dateToCompare);
$storageInvoiceTransactionCreatedDate = Carbon::parse($invoice_transaction->created_at);
Log::info('$storageInvoiceTransactionCreatedDate: '.$storageInvoiceTransactionCreatedDate);
if ($storageInvoiceTransactionCreatedDate->isAfter($dateToCompare) && $invoice_transaction->tax > 0) {
$view = 'pages.pdfs.shipping_invoice_sst';
}
$transaction_invoice_pdf = LaravelMpdf::loadView($view, ['invoice_transaction' => $invoice_transaction]);
$document_object = new DocumentObject(
DocumentType::STORAGE_INVOICE,
@@ -44,24 +44,33 @@ class ReleaseGoodsToCustomerProcessor
/**
* @param $packingList
* @param $invoice
* @param $processCompletedInvoices
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute($packingList, $invoice = null)
public function execute($packingList, $invoice = null, $processCompletedInvoices = false)
{
$result = false;
$totalInvoicesAmountPaid = 0.00;
$totalInvoicesAmount = 0.00;
$invoiceTransactions = $packingList->transactions()->where('status', [ApprovalStatus::APPROVED])->whereIn('type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->get();
if($processCompletedInvoices){
//processCompletedInvoices, for manual processing of invoices, e.g. incomplete payment gateway finish with storage invoice
$invoiceTransactions = $packingList->transactions()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->whereIn('type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->get();
}
else{
$invoiceTransactions = $packingList->transactions()->where('status', ApprovalStatus::APPROVED)->whereIn('type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->get();
}
//Part 1: Process each single invoice type and get the total of all invoices
//Part 1: Process each single invoice type and get the total of all invoices (STORAGE + SHIPPING)
foreach($invoiceTransactions as $invoiceTransaction){
$totalInvoiceAmountPaid = $invoiceTransaction->transactions->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->sum('amount');
if ($invoice && $invoice->type == $invoiceTransaction->type) {
if(($invoice->amount - $totalInvoiceAmountPaid) < 0.01){
Log::channel('storage_invoices')->info('ReleaseGoodsToCustomerProcessor updatesTransactionStatus');
$this->updatesTransactionStatus->execute($invoice, ApprovalStatus::COMPLETED);
if($invoice->type == TransactionType::STORAGE_INVOICE){
//cief todo: remove the following if block
if($invoice->type === TransactionType::STORAGE_INVOICE){
Log::channel('storage_invoices')->info('ReleaseGoodsToCustomerProcessor createStorageInvoiceDocTransactionProcessor');
$this->createStorageInvoiceDocTransactionProcessor->execute($packingList);
}
}
@@ -44,6 +44,8 @@ class CreatesShippingCostTransaction extends AbstractUpdateRelationshipRecord
$model->quantity = $transactionDetailObject->getQuantity();
$model->price = $transactionDetailObject->getPrice();
$model->amount = $transactionDetailObject->getAmount();
$model->tax_amount = $transactionDetailObject->getTaxAmount();
$model->tax_percentage = $transactionDetailObject->getTaxPercentage();
return $this->handler($transaction->transactionDetails(), $model);
@@ -5,8 +5,10 @@ namespace App\Classes\Modules\Transactions\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\General\Eloquent\AbstractUpdateRelationshipRecord;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\PackingList;
use App\Models\Transaction;
use Carbon\Carbon;
class CreatesTransaction extends AbstractUpdateRelationshipRecord
{
@@ -35,4 +37,4 @@ class CreatesTransaction extends AbstractUpdateRelationshipRecord
return $this->handler($packing_list->transactions(), $model);
}
}
}
@@ -23,8 +23,10 @@ class CreatesTransactionDetail extends AbstractUpdateRelationshipRecord
$model->quantity = $object->getQuantity();
$model->price = $object->getPrice();
$model->amount = $object->getAmount();
$model->tax_amount = $object->getTaxAmount();
$model->tax_percentage = $object->getTaxPercentage();
return $this->handler($transaction->transactionDetails(), $model);
}
}
}
@@ -20,8 +20,10 @@ class UpdatesTransactionDetail extends AbstractUpdateRecord
$transactionDetail->quantity = $object->getQuantity();
$transactionDetail->price = $object->getPrice();
$transactionDetail->amount = $object->getAmount();
$transactionDetail->tax_amount = $object->getTaxAmount();
$transactionDetail->tax_percentage = $object->getTaxPercentage();
return $this->handler($transactionDetail);
}
}
}
@@ -0,0 +1,125 @@
<?php
namespace App\Classes\Modules\Wac\ControllersLogic;
use App\Classes\Exceptions\RequestValidationException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounts\DataTransferObjects\RegistrationObject;
use App\Classes\Modules\Companies\DataTransferObjects\EmploymentObject;
use App\Classes\Modules\HelpMenu\Standards\Rules\CanFetchHelpMenuQuestion;
use App\Classes\Modules\HelpMenu\Processors\FetchFirstQuestionQAProcessor;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Http\Resources\HelpMenuQuestionResource;
use App\Models\User;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use App\Classes\Modules\Companies\Processors\AssignEmployeeProcessor;
use App\Classes\Modules\Accounts\Services\CreatesUser;
use App\Classes\ValueObjects\Constants\RoleTypes;
class RegisterExchangeEmailLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification(): array
{
return [
'title' => 'Register Exchange Email',
'message' => 'You have successfully registered an exchange email address'
];
}
/** @var AssignEmployeeProcessor */
private $assignEmployeeProcessor;
/** @var CreatesUser */
private $createsUser;
/**
* RegisterEmailLogic constructor.
* @param AssignEmployeeProcessor $assignEmployeeProcessor
*/
public function __construct(AssignEmployeeProcessor $assignEmployeeProcessor, CreatesUser $createsUser)
{
$this->assignEmployeeProcessor = $assignEmployeeProcessor;
$this->createsUser = $createsUser;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request): JsonResponse
{
// Validate the request inputs
$validator = Validator::make(
$request->all(),
[
'izyim_email' => 'email|required',
'exchange_email' => 'email|required',
]
);
if ($validator->fails()) {
throw new RequestValidationException($validator->messages()->first());
}
// Retrieve the validated emails from the request
$izyimEmail = $request->input('izyim_email');
$exchangeEmail = $request->input('exchange_email');
// Check if both emails are the same
if ($izyimEmail === $exchangeEmail) {
return response()->json([
'message' => 'Both emails are the same.',
'status' => 'ok',
], 200);
}
// Check if the exchange email already exists in the system
$existingUser = User::where('email', $exchangeEmail)->first();
if ($existingUser) {
return response()->json([
'message' => 'The Exchange email already exists in the system.',
'status' => 'error',
], 400);
}
// Retrieve the izyim user
$izyimUser = User::where('email', $izyimEmail)->first();
if (!$izyimUser) {
return response()->json([
'message' => 'The Izyim email not found in system.',
'status' => 'error',
], 404);
}
$newUser = new User();
$newUser->name = $izyimUser->name;
$newUser->email = $exchangeEmail;
$newUser->password = $izyimUser->password;
$newUser->type = RoleTypes::USER;
$newUser->status = ApprovalStatus::APPROVED;
$newUser->save();
Log::channel('wac_webhook')->info('Wac created user: ' . $exchangeEmail);
$companyModule = $izyimUser->companyModule->first();
$Object = new EmploymentObject($companyModule, $newUser);
$this->assignEmployeeProcessor->execute($Object);
return response()->json([
'message' => 'Exchange email registered successfully.',
'status' => 'success',
], 201);
}
}
@@ -15,6 +15,7 @@ use App\Classes\Modules\Transactions\Services\CreatesTransactionableTransaction;
use App\Classes\Modules\Wallets\DataTransferObjects\WalletObject;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance;
use App\Models\CompanyModule;
class CreditWalletProcessor
@@ -36,6 +37,9 @@ class CreditWalletProcessor
/** @var UpdatesWallet */
private $updatesWallet;
/** @var UpdatesWalletBalance */
private $updatesWalletBalance;
/**
* CreateWalletLogic constructor.
@@ -45,6 +49,7 @@ class CreditWalletProcessor
* @param CreatesTransaction $createsTransaction
* @param CreatesTransactionableTransaction $createsTransactionableTransaction
* @param UpdatesWallet $updatesWallet
* @param UpdatesWalletBalance $updatesWalletBalance
*/
public function __construct(
GeneratesWalletCode $generatesWalletCode,
@@ -52,7 +57,8 @@ class CreditWalletProcessor
GeneratesTransactionBillNumber $generatesTransactionBillNumber,
CreatesTransaction $createsTransaction,
CreatesTransactionableTransaction $createsTransactionableTransaction,
UpdatesWallet $updatesWallet
UpdatesWallet $updatesWallet,
UpdatesWalletBalance $updatesWalletBalance
)
{
$this->generatesWalletCode = $generatesWalletCode;
@@ -61,6 +67,7 @@ class CreditWalletProcessor
$this->createsTransaction = $createsTransaction;
$this->createsTransactionableTransaction = $createsTransactionableTransaction;
$this->updatesWallet = $updatesWallet;
$this->updatesWalletBalance = $updatesWalletBalance;
}
@@ -87,11 +94,13 @@ class CreditWalletProcessor
$transaction_object = new TransactionObject($billNumber, $transactionType === 2 ? TransactionType::DEBIT_NOTE : TransactionType::CREDIT_NOTE, 1, $wallet->owner->id, 1, PaymentMethodType::CASH, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], $reference);
$transaction = $this->createsTransactionableTransaction->execute($wallet, $transaction_object);
$updateWalletAmount = $transactionType === 2 ? ($wallet->amount - $transaction->amount) : ($wallet->amount + $transaction->amount);
$this->updatesWalletBalance->execute($wallet, $transaction->amount);
$walletObject = new WalletObject($wallet->owner->id, $wallet->currency_id, $wallet->code, $updateWalletAmount);
// $updateWalletAmount = $transactionType === 2 ? ($wallet->amount - $transaction->amount) : ($wallet->amount + $transaction->amount);
$wallet = $this->updatesWallet->execute($wallet, $walletObject);
// $walletObject = new WalletObject($wallet->owner->id, $wallet->currency_id, $wallet->code, $updateWalletAmount);
// $wallet = $this->updatesWallet->execute($wallet, $walletObject);
return $wallet;
}
@@ -8,6 +8,7 @@ use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\PackingList;
use App\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class InvoiceIssuedEmail extends AbstractEmail
@@ -35,10 +36,14 @@ class InvoiceIssuedEmail extends AbstractEmail
$invoice = $this->packingList->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->where('status', ApprovalStatus::APPROVED)->first();
$invoiceDocument = $invoice->documents()->first()->files;
// $fileContent = Storage::disk('documents')->get($invoiceDocument->first()->file->file_info->original->file);
$filePath = storage_path('app/documents/' . $invoiceDocument->first()->file->file_info->original->file);
Log::info('InvoiceIssuedEmail sent - Att: '.$this->user->name.' - Invoice for order no.'. $this->packingList->owner->reference);
return (new MailMessage)
->subject('Att: '.$this->user->name.' - Invoice for order no.'. $this->packingList->owner->reference)
->attach(Storage::disk('documents')->get($invoiceDocument->first()->file->file_info->original->file), [
->attach($filePath, [
'as' => 'name.pdf',
'mime' => 'application/pdf',
])->view('emails.shipment.invoice', ['user' => $this->user, 'packingList' => $this->packingList]);
@@ -9,6 +9,7 @@ use App\Models\PackingList;
use App\Models\PasswordReset;
use App\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class ShipmentDepartureEmail extends AbstractEmail
@@ -40,10 +41,14 @@ class ShipmentDepartureEmail extends AbstractEmail
$invoiceDocument = $invoice->documents()->first()->files;
// $fileContent = Storage::disk('documents')->get($invoiceDocument->first()->file->file_info->original->file);
$filePath= storage_path('app/documents/' . $invoiceDocument->first()->file->file_info->original->file);
Log::info('ShipmentDepartureEmail sent - Att: '.$this->user->name.' - Invoice for order no.'. $this->packingList->owner->reference);
return (new MailMessage)
->subject('Your packages are on the way to malaysia - Invoice pending payment for order no.'. $this->packingList->owner->reference)
->attach(Storage::disk('documents')->get($invoiceDocument->first()->file->file_info->original->file), [
->attach($filePath, [
'as' => 'name.pdf',
'mime' => 'application/pdf',
])->bcc(['email_test@cief-malaysia.com'])->view('emails.shipment.ETD', ['user' => $this->user, 'packingList' => $this->packingList]);
@@ -0,0 +1,12 @@
<?php
namespace App\Classes\ValueObjects\Constants;
final class TaxPercentage {
public const DEFAULT = 0;
public const NO_TAX = 0;
public const SIX_PERCENT = 6;
}
@@ -13,4 +13,6 @@ final class TransactionDetailType {
public const CUSTOM_CHARGES = 'Custom charges';
public const STORAGE_FEE = 'Malaysia Warehouse Storage Fee';
public const PAY_ON_BEHALF = 'Pay on behalf';
}
@@ -0,0 +1,94 @@
<?php
namespace App\Console\Commands;
use App\Classes\Modules\Billplzs\Processors\CallbackBillplzProcessor;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\Group;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
class FixApprovedPaymentFailedGroup extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'fix-approved-payment-failed-group';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fix all payment is approved or completed but group failed to be updated';
protected $output = null;
protected $outputArray = [];
/** @var CallbackBillplzProcessor */
private $callbackBillplzProcessor;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(CallbackBillplzProcessor $callbackBillplzProcessor)
{
parent::__construct();
$this->callbackBillplzProcessor = $callbackBillplzProcessor;
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
ini_set('memory_limit', '-1');
$this->outputArray = [];
$start = new Carbon();
$groups = Group::whereNotIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('payment', function ($query) {
$query->whereIn('status', [2, 3]);
})->get();
foreach ($groups as $group) {
$transaction = $group->payment;
$response = Http::withBasicAuth(config('billplz.api_key') . ':', '')->get(config('billplz.base_url') . '/api/v3/bills/' . $transaction->payment_reference);
dump($transaction->payment_reference);
if ($response->successful()) {
$data = $response->json();
if ($data['paid']) {
$status = ApprovalStatus::PENDING_VERIFICATION;
if ($data['state'] === 'paid') {
$status = ApprovalStatus::APPROVED;
}
$this->info(Carbon::now() . ' : Fixing ' . $transaction->payment_reference);
$this->callbackBillplzProcessor->execute($transaction, $status);
}
} else {
$this->info("billplz error</br>");
}
}
$end = new Carbon();
$elapsedTime = $start->diff($end)->format('%H:%I:%S');
if ($groups) {
$this->info(Carbon::now() . ' : Done . ElapsedTime: ' . $elapsedTime);
}
}
}
+14 -7
View File
@@ -58,13 +58,20 @@ class FixInvoice extends Command
foreach ($orders as $order){
$invoices = $order->transactions()->where('transactions.type', TransactionType::SHIPPING_INVOICE)->get();
foreach ($invoices as $invoice){
dump($invoice->id);
$invoice->documents()->delete();
$transaction_invoice_pdf = LaravelMpdf::loadView('pages.pdfs.shipping_invoice', ['invoice_transaction' => $invoice]);
$view = 'pages.pdfs.shipping_invoice';
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
$shippingInvoiceTransactionCreatedDate = Carbon::parse($invoice->created_at);
if ($shippingInvoiceTransactionCreatedDate->isAfter($dateToCompare) && $invoice->tax > 0) {
$view = 'pages.pdfs.shipping_invoice_sst';
}
$transaction_invoice_pdf = LaravelMpdf::loadView($view, ['invoice_transaction' => $invoice]);
$document_object = new DocumentObject(
DocumentType::SHIPPING_INVOICE,
[chunk_split('data:application/pdf;base64,'.base64_encode($transaction_invoice_pdf->output()))],
@@ -72,10 +79,10 @@ class FixInvoice extends Command
ApprovalStatus::COMPLETED,
'shipping_invoice'
);
/** @var Document $document */
$document = (App()->make(CreatesDocument::class))->execute($invoice, $document_object);
(App()->make(CreatesFiles::class))->execute($document, $document_object);
}
}
@@ -0,0 +1,54 @@
<?php
namespace App\Console\Commands;
use App\Classes\Modules\PackingLists\Processors\FetchPackingListsFromYdPortalProcessor;
use Carbon\Carbon;
use Illuminate\Console\Command;
class FixMissingPackingList extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'fix-missing-packinglist';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fix Missing Packinglist';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$start_date = '2024-05-05';
$end_date = '2024-05-05';
$start = $start_date ? Carbon::parse($start_date) : null;
$end = $end_date ? Carbon::parse($end_date) : null;
if (!$start || !$end) {
return;
}
(App()->make(FetchPackingListsFromYdPortalProcessor::class))->execute($start, $end);
}
}
@@ -0,0 +1,81 @@
<?php
namespace App\Console\Commands;
use App\Classes\Modules\Billplzs\Processors\CallbackBillplzProcessor;
use App\Classes\Modules\Billplzs\Processors\CallbackBillplzDataPatchProcessor;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\Transaction;
use Carbon\Carbon;
use Illuminate\Console\Command;
class OneTimeTransactionFixBillplzFailedCallback extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'one-time-transaction-fix-billplz-failed-callback';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This is a one time fix billplz failed callback for transaction with id 15205';
protected $output = null;
protected $outputArray = [];
/** @var CallbackBillplzProcessor */
private $callbackBillplzProcessor;
/** @var CallbackBillplzDataPatchProcessor */
private $callbackBillplzDataPatchProcessor;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(CallbackBillplzProcessor $callbackBillplzProcessor, CallbackBillplzDataPatchProcessor $callbackBillplzDataPatchProcessor)
{
parent::__construct();
$this->callbackBillplzProcessor = $callbackBillplzProcessor;
$this->callbackBillplzDataPatchProcessor = $callbackBillplzDataPatchProcessor;
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
ini_set('memory_limit', '-1');
$this->outputArray = [];
$start = new Carbon();
//Transaction fix with this one time fix command: 15205, 16803, 17310
//This transaction, 16803 has approve payment but not its owner, shipping invoice
$transaction = Transaction::whereIn('id', [17310])->first();
$this->info(Carbon::now() . ' : One time fix failled callback from billplz for transaction with id 17310 cron started.');
if($transaction && $transaction->id == 17310){
$this->info(Carbon::now() . ' : 17310.');
$status = ApprovalStatus::APPROVED;
// $this->callbackBillplzProcessor->execute($transaction, $status);
$this->callbackBillplzDataPatchProcessor->execute($transaction, $status);
}
$end = new Carbon();
$elapsedTime = $start->diff($end)->format('%H:%I:%S');
$this->info(Carbon::now() . ' : One time fix failled callback from billplz for transaction with id 17310 cron ended. ElapsedTime: ' . $elapsedTime);
}
}
@@ -7,6 +7,7 @@ use App\Models\PermitsReminder;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class SendPermitsReminderEmails extends Command
{
@@ -41,23 +42,23 @@ class SendPermitsReminderEmails extends Command
*/
public function handle()
{
$now = now();
$startRange = $now->subDays(3)->startOfDay();
$endRange = $now->copy()->addDays(6)->endOfDay();
$currentDateTime = now();
$startRange = $currentDateTime->subDays(3)->startOfDay();
$endRange = $currentDateTime->copy()->addDays(6)->endOfDay();
$reminders = PermitsReminder::whereDate('reminder_date', '>=', $startRange)
->whereDate('reminder_date', '<=', $endRange)
->orWhere('reminder_date', '<', now())
->pluck('model');
$this->info($this->getTimeStamp() . json_encode($reminders));
if ($reminders->isEmpty()) {
$this->info('No reminders expiring within the specified range.');
return;
}
$users = User::whereIn(
'email',
[
'edmond.wuiming2021@gmail.com',
'anithagurl96@gmail.com'
]
)->get();
$this->info($this->getTimeStamp() . 'Reminders: ' . $reminders->toJson());
$users = User::whereIn('email', $this->getEmailList())->get();
foreach ($users as $user) {
$this->info($this->getTimeStamp() . 'Reminder email sent to ' . $user->email);
@@ -71,4 +72,12 @@ class SendPermitsReminderEmails extends Command
{
return '[' . Carbon::now()->format('Y-m-d H:i:s') . '] - ';
}
protected function getEmailList(): array
{
return [
// 'edmond.wuiming2021@gmail.com',
'anithagurl96@gmail.com'
];
}
}
+36 -36
View File
@@ -28,50 +28,50 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule)
{
$schedule->command('command:curlVTCommand')
->cron('0 8 * * *')
->withoutOverlapping()
->appendOutputTo (storage_path().'/logs/curlvt.log');
$schedule->command('command:curlYdOrderListCommand')
->cron('0 9-18/3 * * *')
->withoutOverlapping()
->appendOutputTo (storage_path().'/logs/curlyd.log');
$schedule->command('fix-packinglist')
->cron('30 9-18/3 * * *')
->withoutOverlapping()
->appendOutputTo (storage_path().'/logs/fix_packinglist.log');
// $schedule->command('command:curlVTCommand')
// ->cron('0 8 * * *')
// ->withoutOverlapping()
// ->appendOutputTo (storage_path().'/logs/curlvt.log');
// $schedule->command('command:curlYdOrderListCommand')
// ->cron('0 9 * * *')
// ->cron('0 9-18/3 * * *')
// ->withoutOverlapping()
// ->appendOutputTo (storage_path().'/logs/departure_email.log');
// ->appendOutputTo (storage_path().'/logs/curlyd.log');
$schedule->command('fix-duplicate-container-reference')
->cron('0 1 * * *')
->withoutOverlapping()
->appendOutputTo (storage_path().'/logs/fix_duplicate_container_reference.log');
// $schedule->command('fix-packinglist')
// ->cron('30 9-18/3 * * *')
// ->withoutOverlapping()
// ->appendOutputTo (storage_path().'/logs/fix_packinglist.log');
$schedule->command('invoice:generate')
->hourly()
->withoutOverlapping()
->appendOutputTo (storage_path().'/logs/auto_generate_invoice.log');
// // $schedule->command('command:curlYdOrderListCommand')
// // ->cron('0 9 * * *')
// // ->withoutOverlapping()
// // ->appendOutputTo (storage_path().'/logs/departure_email.log');
$schedule->command('billplz-failed-callback:fix')
->hourly()
->withoutOverlapping()
->appendOutputTo (storage_path().'/logs/fix_failed_callback_from_billplz.log');
// $schedule->command('fix-duplicate-container-reference')
// ->cron('0 1 * * *')
// ->withoutOverlapping()
// ->appendOutputTo (storage_path().'/logs/fix_duplicate_container_reference.log');
$schedule->command('check-storage-invoices-group-transactions')
->dailyAt('0:01')
->withoutOverlapping()
->appendOutputTo(storage_path().'/logs/check_storage_invoices.log');
// $schedule->command('invoice:generate')
// ->hourly()
// ->withoutOverlapping()
// ->appendOutputTo (storage_path().'/logs/auto_generate_invoice.log');
$schedule->command('permitsReminder:send')
->dailyAt('09:30')
->withoutOverlapping()
->appendOutputTo(storage_path().'/logs/permits-reminder-send.log');
// $schedule->command('billplz-failed-callback:fix')
// ->hourly()
// ->withoutOverlapping()
// ->appendOutputTo (storage_path().'/logs/fix_failed_callback_from_billplz.log');
// $schedule->command('check-storage-invoices-group-transactions')
// ->dailyAt('0:01')
// ->withoutOverlapping()
// ->appendOutputTo(storage_path().'/logs/check_storage_invoices.log');
// $schedule->command('permitsReminder:send')
// ->dailyAt('09:30')
// ->withoutOverlapping()
// ->appendOutputTo(storage_path().'/logs/permits-reminder-send.log');
}
/**
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Exports;
use App\Classes\Modules\Exports\Services\ExportsCustomersOrderLatestDate;
use App\Classes\Modules\Exports\Services\ExportsCustomerTotalOrderByYear;
use App\Classes\Modules\Exports\Services\ExportsGroupTransactions;
use App\Classes\Modules\Exports\Services\ExportsPaymentTransactions;
use App\Models\User;
use Illuminate\Http\Request;
@@ -33,6 +34,13 @@ class ExportCustomersToExcelController
return $response;
}
public function groupTransaction(Request $request){
$exportsTransactions = new ExportsGroupTransactions($request);
$response = $exportsTransactions->download('group-transactions.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
ob_end_clean();
return $response;
}
public function totalOrders(Request $request){
$exportsTotalOrders = new ExportsCustomerTotalOrderByYear($request);
$response = $exportsTotalOrders->download('total-orders-' . $request->route('year') . '.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
@@ -40,25 +40,41 @@ class ImportPermitsReminderController
$row['reminder_date'] = $this->changeExcelDate($row['reminder_date']);
$validator = Validator::make($row, [
'model' => 'required|unique:permits_reminders,model',
'expiry_date' => 'required|date|after_or_equal:today',
'reminder_date' => 'required|date|after_or_equal:today',
'model' => 'required',
// 'expiry_date' => 'required|date|after_or_equal:today',
'expiry_date' => 'required|date',
// 'reminder_date' => 'required|date|after_or_equal:today',
'reminder_date' => 'required|date',
]);
if ($validator->fails()) {
$row['status'] = 'failed';
$row['message'] = $validator->errors()->all();
$returnArray[] = $row;
} else {
$row['created_at'] = Carbon::now();
$row['updated_at'] = Carbon::now();
$successRows[] = $row;
$existingRecord = PermitsReminder::where('model', $row['model'])->first();
if ($existingRecord) {
$existingRecord->update([
'expiry_date' => $row['expiry_date'],
'reminder_date' => $row['reminder_date'],
]);
$row['status'] = 'Updated';
$row['message'] = 'Record has been updated';
$returnArray[] = $row;
} else {
// Add to successful rows for batch insertion
$row['created_at'] = Carbon::now();
$row['updated_at'] = Carbon::now();
$successRows[] = $row;
}
}
}
// Insert only if there are new successful rows
if (!empty($successRows)) {
PermitsReminder::insert($successRows); // Insert only if there are successful rows
PermitsReminder::insert($successRows);
}
$successCount = count($successRows);
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\PermitsReminder;
use App\Models\PermitsReminder;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -15,14 +16,25 @@ class ListPermitsReminderController
public function list(Request $request): JsonResponse
{
$perPage = 10; // Number of items per page
$page = request('page', 1); // Get the current page from the request
$page = $request->query('page', 1); // Get the current page from the request
// Paginate the PermitsReminder model
$query = PermitsReminder::orderBy('id', 'desc');
$paginator = $query->paginate($perPage, ['*'], 'page', $page);
$paginator = PermitsReminder::orderBy('reminder_date')->paginate($perPage, ['*'], 'page', $page);
// Convert paginated data to an array
// Get current date and time
$currentDateTime = now();
// Define date range
$startRange = $currentDateTime->subDays(3)->startOfDay();
$endRange = $currentDateTime->copy()->addDays(6)->endOfDay();
// Process paginated items
$data = $paginator->items();
foreach ($data as &$item) {
// Check if the reminder_date is within the date range or after the current date
$reminderDate = Carbon::parse($item->reminder_date);
$item->show_remind = $reminderDate->between($startRange, $endRange) || $reminderDate->isBefore($currentDateTime);
}
// Create pagination links
$links = [
@@ -32,6 +44,7 @@ class ListPermitsReminderController
"next" => $paginator->nextPageUrl(),
];
// Prepare response
$response = [
"title" => "List Permits Reminder",
"message" => "You have successfully retrieved a list of Permits Reminder",
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\DeletePaidGroupLogic;
class DeletePaidGroupController
{
/**
* @param Request $request
* @param DeleteGroupTransactionLogic $logic
* @return JsonResponse
*/
public function delete(Request $request, DeletePaidGroupLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Wac;
use App\Classes\Modules\Wac\ControllersLogic\RegisterExchangeEmailLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class RegisterExchangeEmailController
{
/**
* @param Request $request
* @param FetchQuestionQALogic $logic
* @return JsonResponse
*/
public function register(Request $request, RegisterExchangeEmailLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -29,7 +29,12 @@ class CheckForStorageInvoiceByOrderId
public function handle(Request $request, Closure $next)
{
$orderId = $request->route('id');
$storages = $this->storageInvoiceTransactionProcessor->execute($orderId);
$storages = [];
try{
$storages = $this->storageInvoiceTransactionProcessor->execute($orderId);
} catch (\Exception $exception) {
$storages = [];
}
$request->merge(['storages' => $storages]);
return $next($request);
}
+2 -1
View File
@@ -40,7 +40,8 @@ class CompanyResource extends JsonResource
'last_order' => $companyModule->type === 7 ? new DeliveryOrderResource($companyModule->orders()->orderBy('id', 'DESC')->first()) : new OrderResource($companyModule->orders()->orderBy('id', 'DESC')->first()),
'order_count' => $companyModule->orders()->count(),
'identification' => new DocumentResource($this->documents->whereIn('document_type', DocumentType::IDENTIFICATION_DOCUMENTS)->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->first()),
'created_at' => $this->created_at->format('d-m-Y')
'created_at' => $this->created_at->format('d-m-Y'),
'whatsapp' => new ContactResource($this->contacts()->where('reference', 'LIKE', '%Whatsapp%')->orderBy('created_at', 'DESC')->first())
];
+14 -6
View File
@@ -23,17 +23,25 @@ class ContainerResource extends JsonResource
'container_specification' => ContainerTypes::CONTAINER_SPECIFICATION[$this->container_type],
'container_number' => $this->container_number,
'seal_reference' => $this->seal_reference,
'loading_date' => $this->loading_date ? $this->loading_date->format('d-m-Y') : $this->loading_date,
'loading_date' => optional($this->loading_date)->format('d-m-Y'),
'transport' => new TransportResource($this->transports()->first()),
'packages_count' => $this->packages()->where('packages.type', '!=', 2)->sum('quantity'),
'total_cbm' => $this->packages()->sum(DB::raw('(width/100) * (height/100) * (length/100) * quantity')),
'remarks' => RemarkResource::collection($this->remarks),
'status' => $this->status,
'packing_lists' => $this->whenLoaded('packingLists', PackingListResource::collection($this->whenLoaded('packingLists', function(){
return $this->packingLists()->get()->sortBy(function ($packingList) {
return $packingList->owner->company_module_id;
});
})))
'packing_lists' => $this->shouldIncludePackingListData($request) ? $this->getSortedPackingLists() : null,
];
}
protected function shouldIncludePackingListData($request)
{
return (isset($request->all()['with_packing_list_data']) && ($request->all()['with_packing_list_data'] == false)) ? false : true;
}
protected function getSortedPackingLists()
{
return $this->whenLoaded('packingLists', PackingListResource::collection(
$this->packingLists()->get()->sortBy(fn ($packingList) => $packingList->owner->company_module_id)
));
}
}
@@ -32,9 +32,11 @@ class MappableTransactionWithDetailsResource extends JsonResource
if ($this->type === TransactionType::PAYMENT) {
$order = $this->owner->owner->owner;
$data['order_reference'] = $order->reference;
$data['debtor_code'] = $order->companyModule->company->debtor;
$data['created_at'] = $this->owner->created_at; // invoice date
if ($order) {
$data['order_reference'] = $order->reference;
$data['debtor_code'] = $order->companyModule->company->debtor;
$data['created_at'] = $this->owner->created_at; // invoice date
}
} elseif (in_array($this->type, [TransactionType::GROUP_PAYMENT, TransactionType::TOP_UP])) {
$connection = $this->owner->owner->inviters()->withPivot('invitee_reference')->first();
$data['marking'] = $connection ? $connection->pivot->invitee_reference : '';
@@ -48,17 +50,20 @@ class MappableTransactionWithDetailsResource extends JsonResource
if ($payments->count()) {
$data['payment_transactions'] = $payments->map(function ($payment) {
$order = $payment->owner->owner->owner;
return [
'id' => $payment->id,
'updated_at' => $payment->updated_at,
'debtor_code' => $order->companyModule->company->debtor,
'order_reference' => $order->reference,
'bill_no' => null,
'type' => $payment->type,
'marking' => null,
'amount' => $payment->amount,
'created_at' => $this->created_at
];
if ($order) {
return [
'id' => $payment->id,
'updated_at' => $payment->updated_at,
'debtor_code' => $order->companyModule->company->debtor,
'order_reference' => $order->reference,
'bill_no' => null,
'type' => $payment->type,
'marking' => null,
'amount' => $payment->amount,
'created_at' => $this->created_at
];
}
})->toArray();
} else {
$data['status'] = 'error';
+1 -1
View File
@@ -18,7 +18,7 @@ class SegmentResource extends JsonResource
$constant = $this->constants->where('reference', SegmentConstants::CUSTOM_PRICE)->first();
return [
'id' => $this->id,
'name' => $this->name,
'name' => ucwords(str_replace('_', ' ', $this->name)),
'price' => $constant ? $constant->value[0] : 0
];
}
@@ -5,6 +5,7 @@ namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Group;
use App\Models\PackingList;
use App\Models\Transaction;
use App\Models\Wallet;
use Carbon\Carbon;
@@ -27,7 +28,9 @@ class TransactionWithStorageResource extends JsonResource
$groupPaymentAttemptsFiltered = [];
$group_payment_expired = null;
$group_payment_history = null;
$group_payment_history_query = null;
$groupTotalAmount = 0;
$payment_history = null;
if ($this->owner instanceof Transaction) {
if ($this->owner) {
@@ -43,7 +46,8 @@ class TransactionWithStorageResource extends JsonResource
if($this->groups){
$group_payment_attempts = GroupForOrderV2Resource::collection($this->groups->whereNotIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]));
$group_payment_expired = GroupForOrderV2Resource::collection($this->groupsWithTrashed->whereIn('status', [ApprovalStatus::EXPIRED]));
$group_payment_history = GroupForOrderV2Resource::collection($this->groups->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::REJECTED]));
$group_payment_history_query = $this->groups->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::REJECTED]);
$group_payment_history = GroupForOrderV2Resource::collection($group_payment_history_query);
}
} else {
@@ -54,9 +58,13 @@ class TransactionWithStorageResource extends JsonResource
}
$packingListReference = null;
if ($this->owner instanceof PackingList) {
$packingListReference = $this->owner->reference;
}
$ts = $this->groups->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION])->last();
if ($ts) {
if ($ts && $group_payment_history && $group_payment_attempts) {
$paymentTransaction = Transaction::where('payment_reference', $ts->reference)->whereIn('status', [ApprovalStatus::PENDING_SUBMISSION])->first();
if($paymentTransaction){
$groupTotalAmount = (double) $this->amount;
@@ -76,6 +84,21 @@ class TransactionWithStorageResource extends JsonResource
}
$payment_history = TransactionResource::collection($this->transactions()
->payments()
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::COMPLETED, ApprovalStatus::REJECTED])
->get());
//For 'Your Payment Proof' at frontend
if($group_payment_history_query && count($group_payment_history_query) > 0){
if($this->getReferenceForGroupPayment($group_payment_history_query)){
foreach ($payment_history as $item) {
$item['payment_reference'] = $this->getReferenceForGroupPayment($group_payment_history_query);
}
}
}
return [
'id' => $this->id,
'owner_type' => $this->owner_type,
@@ -112,13 +135,9 @@ class TransactionWithStorageResource extends JsonResource
->payments()->where('status', ApprovalStatus::EXPIRED)
->get()
),
'payment_history' => TransactionResource::collection(
$this->transactions()
->payments()
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::COMPLETED, ApprovalStatus::REJECTED])
->get()
),
'payment_history' => $payment_history,
'remarks' => RemarkResource::collection($this->remarks),
'packing_list_reference' => $packingListReference,
'storages' => $this->storages ? $this->storages : null, //from middleware
'is_waived' => (int) $this->is_waived,
'expires_on' => Carbon::parse($this->expires_on)->format('d-m-Y h:i:s A'),
@@ -126,4 +145,12 @@ class TransactionWithStorageResource extends JsonResource
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y')
];
}
private function getReferenceForGroupPayment($groups){
if (is_array($groups) && count($groups) > 0) {
$firstGroup = $groups[0];
return $firstGroup['reference'];
}
return null;
}
}
+8
View File
@@ -69,4 +69,12 @@ class CompanyConnection extends AbstractModel
return $this->belongsToMany(Segment::class, (new ConnectionSegment())->getTable(), 'company_connection_id', 'segment_id');
}
/**
* @return belongsToMany
*/
public function connectionSegments(): HasMany
{
return $this->hasMany(ConnectionSegment::class, 'company_connection_id');
}
}
+8
View File
@@ -84,4 +84,12 @@ class Group extends Model implements Documentable, Transactionable
{
return $this->BelongsTo(Currency::class, 'original_currency_id', 'id');
}
/**
* @return hasOne
*/
public function payment()
{
return $this->hasOne(Transaction::class, 'payment_reference', 'reference');
}
}
+23
View File
@@ -112,6 +112,29 @@ return [
'path' => storage_path('logs/paymentUnknownOrderLog.log'),
'level' => 'info',
],
'deletePaidGroupOrder' => [
'driver' => 'single',
'path' => storage_path('logs/deletePaidGroupOrder.log'),
'level' => 'info',
],
'vue_polling' => [
'driver' => 'single',
'path' => storage_path('logs/laravel_vue_plling.log'),
'level' => 'info',
],
'perfex_crm' => [
'driver' => 'single',
'path' => storage_path('logs/laravel_perfex_crm.log'),
'level' => 'info',
],
'wac_webhook' => [
'driver' => 'single',
'path' => storage_path('logs/wac_webhook.log'),
'level' => 'info',
],
],
];
+8
View File
@@ -0,0 +1,8 @@
<?php
return [
// please update resources/assets/vue/app.js if value is changed
// 'qr_code_img_url' => 'https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=', // not working
'qr_code_img_url' => 'https://qrcode.tec-it.com/API/QRCode?data=',
];
+6
View File
@@ -0,0 +1,6 @@
<?php
return [
'account_registration_url' => 'https://wacontact.readyspace.com/rest/trigger/9fe03daf-2ace-4095-b064-601ef6d9bfe7',
'invoice_created_url' => 'https://wacontact.readyspace.com/rest/trigger/c0d38aa7-91ac-458b-8014-cf3287a636e4',
];
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddColumnsToTransactionDetailsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('transaction_details', function (Blueprint $table) {
$table->decimal('tax_amount', 14, 5)->after('amount')->default(0.00);
$table->decimal('tax_percentage', 6, 2)->after('tax_amount')->default(0.00);
$table->decimal('discount', 14, 5)->after('tax_percentage')->default(0.00);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('transaction_details', function (Blueprint $table) {
$table->dropColumn('tax_amount');
$table->dropColumn('tax_percentage');
$table->dropColumn('discount');
});
}
}
+3
View File
@@ -24,6 +24,9 @@ import closable from './general/directives/closable';
import Avatar from 'vue-avatar';
// define global variable
Vue.prototype.$qr_code_img_url = 'https://qrcode.tec-it.com/API/QRCode?data=';
/** Application Injections */
Vue.use(vuelidate);
Vue.use(VueTheMask);
@@ -10,6 +10,10 @@
<div class="font-heading all-caps fs-11">{{this.item.name}}</div>
<div class="font-heading all-caps fs-11">CIEF/{{this.item.company_module.marking}}</div>
</div>
<div class="col-auto" v-if="this.item.whatsapp">
<div class="font-heading all-caps fs-11">Whatsapp:</div>
<div class="font-heading all-caps fs-11">{{ this.item.whatsapp.reference.replace('Whatsapp:', '') }}: {{ this.item.whatsapp.phone }}</div>
</div>
<div class="col-auto text-center">
<div class="font-heading all-caps fs-11">Orders</div>
<div class="font-heading all-caps fs-11">{{this.item.order_count}}</div>
@@ -116,9 +116,10 @@
},
sumAmount () {
var new_object = this.selectedInvoice;
return Object.keys(new_object).reduce(function(total, key) {
return total + Math.round(new_object[key].amount * 100) / 100;
var total = Object.keys(new_object).reduce(function(total, key) {
return total + new_object[key].amount;
}, 0).toFixed(2);
return Math.round(total * 100) / 100;
},
selectedIds () {
return this.selectedInvoice.map(s=>s.id);
@@ -0,0 +1,115 @@
<template>
<div class="row bg-white padding-25">
<div class="col" v-if="step === 1">
<loading-component style="height: 300px; top: 0;" key="1" color="success" v-show="isLoading" ></loading-component>
<div class="row justify-content-center" v-show="!isLoading">
<div class="col">
<div class="row">
<div class="col">
<h3>Please provide your Whatsapp phone number:</h3>
</div>
</div>
<div class="row m-t-20 m-b-15">
<div class="col-12">
<validation-wrapper-component :validator="$v.parameters.name">
<label>Name</label>
<input class="form-control" v-model="parameters.name">
</validation-wrapper-component>
</div>
<div class="col-12 p-t-15">
<validation-wrapper-component :validator="$v.parameters.phone">
<label>Phone</label>
<input class="form-control" v-model="parameters.phone">
</validation-wrapper-component>
</div>
</div>
<div class="row p-t-15">
<div class="col p-r-5">
<div class="btn btn-lg block btn m-b-5 no-border bg-master-lighter" data-dismiss="modal">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-lg block btn m-b-5 no-border text-white bg-primary pointer" @click="nextStep">Confirm</div>
</div>
</div>
</div>
</div>
</div>
<div class="col" v-if="step === 2">
<loading-component style="height: 300px; top: 0;" key="1" color="success" v-show="isLoading" ></loading-component>
<div class="row justify-content-center" v-show="!isLoading">
<div class="col">
<div class="row">
<div class="col">
<h3>欲知更多详情请浏览 <a href="https://bit.ly/4a5v34p" target="_blank" class="text-underline">https://bit.ly/4a5v34p</a> Whatsapp: <a href="https://wa.me/601136814520" target="_blank" class="text-underline text-success">+6011-36814520</a></h3>
</div>
</div>
<div class="row p-t-15">
<div class="col p-r-5">
<div class="btn btn-lg block btn m-b-5 no-border bg-master-lighter" @click="previousStep">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-lg block btn m-b-5 no-border text-white bg-primary pointer" @click="activateService">Confirm</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { required } from 'vuelidate/lib/validators';
import componentHandler from '../../../general/mixins/componentHandler';
import modalFormHandler from '../../../general/mixins/modalFormHandler';
export default {
props: {
company:{
type: Object,
required: true
},
segmentId:{
type: Number,
required: false
}
},
data() {
return {
step: 1,
parameters: {
name: '',
phone: '',
}
}
},
validations: {
parameters: {
name: { required },
phone: { required },
}
},
methods: {
previousStep() {
this.step = 1;
},
nextStep() {
if (this.parameters.name && this.parameters.phone) {
this.step = 2;
} else {
this.parameters = {};
this.submit(this.route('api.company.connection.assign', this.company.id, this.company.company_module.connections[0].id), 'post', 'activateService', true, false);
}
},
activateService(){
this.parameters.segment_id = this.segmentId
this.submit(this.route('api.company.connection.assign', this.company.id, this.company.company_module.connections[0].id), 'post', 'activateService', true, false);
},
successHandler(){
location.reload();
}
},
mixins: [componentHandler, modalFormHandler]
}
</script>
@@ -0,0 +1,119 @@
<template>
<div class="row m-t-15 m-b-15 align-items-end parentContainer" v-if="company">
<div class="col">
<!-- 1688 consent -->
<div class="row m-l-0 m-r-0 m-b-15 animate__animated animate__tada animate__repeat-2 animate__delay-3s" v-if="!company.segments.some(item => item.id === 10 || item.id === 11 || item.id === 12)">
<div class="col bg-white padding-15">
<div class="row">
<div class="col">
<div class="row ">
<div class="col">
<h5 class="m-t-0 text-justify">重磅消息! CIEF 正式开通 <span class="bold">3PL fulfillment 服务</span>!! 你从中国进来的货物, 我们可以直接帮你仓储, 分拣打包, 直接送货到您的顾客手上, 只需系统对接你的 Lazada , Shopee , Tiktok 网店, 就能实现全自动化! 一个月只需 <span class="bold">RMX88</span>! </h5>
<h5 class="m-t-0 text-justify">Exciting news! CIEF has officially launched its <span class="bold">3PL fulfillment service</span>! We can assist with warehousing, sorting, packaging, and direct delivery of your goods from China to your customers. Simply integrate your Lazada, Shopee, or TikTok online stores with our system for full automation! Only <span class="bold">RMX88</span> per month!</h5>
</div>
</div>
<div class="row">
<div class="col-12 text-center">
<div class="btn btn-md block btn m-b-5 no-border text-white pointer bg-success requestModal" data-type="fulfilment-interested-now">Interested now (感兴趣)</div>
</div>
<div class="col-12 text-center">
<div class="btn btn-md block btn m-b-5 pointer requestModal" data-type="fulfilment-interested-later">Interested later (感兴趣但现在不需要)</div>
</div>
<div class="col-12 text-center">
<div class="btn btn-md block btn m-b-5 no-border text-white pointer bg-danger requestModal" data-type="not-interested-confirm">Not interested (不感兴趣)</div>
</div>
</div>
</div>
</div>
</div>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="fulfilment-interested-now" styleType="fill-in" size="large">
<fulfilment-interested-form-component
section="activateService"
:company="company"
:segmentId="10"
>
</fulfilment-interested-form-component>
</modal-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="fulfilment-interested-later" styleType="fill-in" size="large">
<fulfilment-interested-form-component
section="activateService"
:company="company"
:segmentId="11"
>
</fulfilment-interested-form-component>
</modal-component>
<modal-component small type="not-interested-confirm">
<div class="row">
<div class="col text-center">
<div class="row">
<div class="col text-center">
<div class="row m-b-20">
<div class="col">
<h5 class="all-caps">Are you Sure?</h5>
<div class="fs-11">Are you sure you are not interested in this service?</div>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div data-dismiss="modal" class="btn btn-sm btn-default bg-master-lighter btn-block b-rad-none">No, take me back</div>
</div>
<div class="col p-l-5">
<div data-dismiss="modal" class="btn btn-sm btn-danger btn-block b-rad-none" @click="activateService(12)">Yes, Im sure</div>
</div>
</div>
</div>
</div>
</div>
</div>
</modal-component>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
data(){
return {
section: 'newServiceList',
parameters: {
segment_id: ''
},
expanded: false,
failed: false,
part: 0,
isFetchCompany: false,
company: null
}
},
created(){
this.fetchCompany();
},
methods: {
fetchCompany(){
this.parameters = null;
this.isFetchCompany = true;
this.submit(route('api.company.show', this.$store.getters.getCompanyId), 'get', this.section, false, false)
},
activateService(segmentId){
this.parameters = {
segment_id: segmentId
};
this.part = 1;
this.submit(this.route('api.company.connection.assign', this.company.id, this.company.company_module.connections[0].id), 'post', 'activateService', true, false);
},
successHandler(response){
if (this.isFetchCompany) {
this.company = response.payload.data;
this.isFetchCompany = false;
}
if(this.part == 1 || this.part == 4){
location.reload();
}
}
},
mixins: [componentHandler]
}
</script>
@@ -1,5 +1,5 @@
<template>
<div class="row m-t-5">
<div class="row m-t-5" @keyup.enter="submitForm">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.content">
<input type="text" class="form-control fs-12" placeholder="Write your comments..." v-model.trim="parameters.content">
@@ -42,7 +42,7 @@
submitForm() {
this.submit(this.data ? this.route('api.remark.update', this.data.id) : this.route('api.packing_list.container.remark.create', this.id), this.data ? 'put' : 'post', this.section, true, true);
this.$emit('submit')
}
},
},
mixins: [modalFormHandler]
}
@@ -0,0 +1,229 @@
<template>
<div class="row">
<div class="col">
<div class="row" v-if="container">
<div class="col">
<div class="row align-items-center m-t-10">
<div class="col bg-master-lightest rounded padding-20 m-l-10 m-r-10">
<div class="row d-flex justify-content-between align-items-center">
<div class="col b-r">
<div class="row">
<div class="col">
<p class="m-b-0 large-text normal m-b-10">Container Number</p>
</div>
</div>
<div class="row">
<div class="col">
<p class="m-t-0 m-b-0 fs-20 bold">{{ container.container_reference }}</p>
<p class="m-t-0 m-b-0 small-text">{{ container.container_number }}</p>
<p class="m-t-0 m-b-0 small-text">seal number</p>
</div>
</div>
</div>
<div class="col">
<div class="row">
<div class="col">
<p class="fs-10 bold m-b-0">ETD</p>
<p class="small-text m-t-0">{{ container.transport.current_schedule.etd }} <a class="b-rad-none all-caps requestModal" data-type="rescheduleDate"><i class="fa fa-edit pointer fa-fw fs-15 m-l-5"></i></a>
</p>
</div>
</div>
<div class="row">
<div class="col">
<p class="fs-10 bold m-b-0">ETA</p>
<p class="small-text m-t-0">{{ container.transport.current_schedule.eta }} <a class="b-rad-none all-caps requestModal" data-type="rescheduleDate"><i class="fa fa-edit pointer fa-fw fs-15 m-l-5"></i></a>
</p>
</div>
</div>
</div>
<div class="col">
<div class="row">
<div class="col">
<p class="fs-10 bold m-b-0">Location</p>
<p class="small-text m-t-0">-tbc-</p>
</div>
</div>
<div class="row">
<div class="col">
<p class="fs-10 bold m-b-0">Destination</p>
<p class="small-text m-t-0">-tbc-</p>
</div>
</div>
</div>
<div class="col">
<div class="row">
<div class="col">
<p class="fs-10 bold m-b-0">Container Type</p>
<p class="small-text m-t-0">{{ container.container_specification.name }}</p>
</div>
</div>
<div class="row">
<div class="col">
<p class="fs-10 bold m-b-0">Owner</p>
<p class="small-text m-t-0">-tbc-</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row m-t-10 m-b-50">
<div class="col">
<div class="row">
<div class="col">
<div class="row h-100">
<div class="col">
<h5 class="m-b-5 large-text bold l-20">Tracking:</h5>
<div class="row padding-10">
<div class="col rounded bg-master-lighter padding-15">
<li class="m-t-10">
<a class="btn btn-sm btn-primary b-rad-none all-caps requestModal"
data-type="containerDepature">Container Departure</a>
</li>
<li class="m-t-10">
<a class="btn btn-sm btn-primary b-rad-none all-caps requestModal"
data-type="containerArrival">Arrival Date</a>
</li>
<li class="m-t-10">
<a class="btn btn-sm btn-primary b-rad-none all-caps requestModal"
data-type="containerUnpacking">Date of Unpacking</a>
</li>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in"
type="rescheduleDate">
<reschedule-date-form-component :section="section"
:data="container"></reschedule-date-form-component>
</modal-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in"
type="containerDepature">
<container-depature-form-component :section="section"
:data="container"></container-depature-form-component>
</modal-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in"
type="containerArrival">
<container-arrival-form-component :section="section"
:data="container"></container-arrival-form-component>
</modal-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in"
type="containerUnpacking">
<container-unpacking-form-container :section="section"
:data="container"></container-unpacking-form-container>
</modal-component>
<div class="row m-t-10">
<div class="col">
<div class="row">
<div class="col-auto padding-5 m-l-15 m-r-15 pointer" :class="[{ 'text-primary': tabShowing === 'Comments' }, { 'b-b': tabShowing === 'Comments' }]" @click="tabShowing = 'Comments'">
<h5 class="large-text bold l-20" :class="[{ 'text-primary': tabShowing === 'Comments' }]"> Comments</h5>
</div>
<!-- <div class="col-auto padding-5 m-l-15 m-r-15 pointer" :class="[{ 'text-primary': tabShowing === 'Timeline' }, { 'b-b': tabShowing === 'Timeline' }]" @click="tabShowing = 'Timeline'">
<h5 class="large-text bold l-20" :class="[{ 'text-primary': tabShowing === 'Timeline' }]"> Activity Timeline</h5>
</div> -->
</div>
<div class="row p-l-10 p-r-10">
<div class="col rounded padding-15">
<div class="row" v-if="tabShowing === 'Comments'">
<div class="col">
<div class="row" v-for="remarks in container.remarks">
<div class="col">
<container-comment-component :section="section" :data="remarks"></container-comment-component>
</div>
</div>
<container-comment-form-component :section="section" :id="container.id"></container-comment-form-component>
</div>
</div>
<div class="row" v-if="tabShowing === 'Timeline'">
<div class="col">
<div class="row align-items-center justify-content-center p-t-50 p-b-50">
<div class="col-10">
<div class="row align-items-center justify-content-center hint-text">
<div class="col-4 hint-text"><img src="/images/not-found-illustration.png" class="w-100 hint-text"/></div>
</div>
<div class="row text-center">
<div class="col">
<div class="row m-t-20">
<div class="col">
<p class="all-caps no-margin fs-11" style="letter-spacing: 2px;">Nothing To Show Here</p>
</div>
</div>
<div class="row m-t-5 align-items-center justify-content-center hide">
<div class="col">
<small class="fs-9 muted all-caps font-lato" style="letter-spacing: 2px">There is no results found, Try adjusting your filters to find what you are looking for.</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
id: {
type: Number,
required: true,
}
},
data() {
return {
section: 'containerPofileSection',
isLoading: true,
container: null,
expanded: false,
CBMcapacity: 30,
// tabShowing: 'Timeline',
tabShowing: 'Comments',
selectAllPackingList: false,
selectedPackageList: [],
deleteCommentId: '',
}
},
computed: {
pendingQueue() {
return this.$store.getters.isInCompleteQueue(this.section);
},
loadedCBM() {
return (Math.ceil((this.container.packing_lists.reduce((total, obj) => total + obj.packages.reduce((total, obj) => obj.cbm + total, 0), 0)) * 1000) / 1000).toFixed(3)
},
},
watch: {
pendingQueue(inComplete) {
if (inComplete) {
this.fetchCompany();
}
}
},
created() {
this.$store.dispatch('updateListQueue', { 'name': this.section });
},
methods: {
fetchCompany() {
this.isLoading = true;
this.submit(route('api.packing_list.container.show', this.id) + '?with_packing_list_data=0', 'get', this.section, false, false)
},
successHandler(response) {
this.$store.dispatch('completeList', { 'name': this.section, 'data': [] });
this.isLoading = false;
this.container = response.payload.data;
},
}
}
</script>
@@ -74,9 +74,6 @@
data(){
return {
filters: this.options,
pollingInterval: null,
isPolling: false,
isFetchingResult: false,
isLoading: false,
}
},
@@ -87,13 +84,26 @@
computed: {
pendingList () {
return this.$store.getters.isInCompleteQueue(this.section);
}
},
getJob() {
return this.$store.getters.getJob(this.section);
},
getJobAttemptCount() {
return this.$store.getters.getJobAttemptCount(this.section);
},
},
watch: {
pendingList(inComplete){
if(inComplete){
this.fetchList();
}
},
getJobAttemptCount(newValue, oldValue) {
// console.log('Old value:', JSON.stringify(oldValue));
// console.log('New value:', JSON.stringify(newValue));
if(this.getJob){
this.fetchJobResult(this.getJob.isLastAttempt);
}
}
},
methods: {
@@ -101,7 +111,7 @@
let listDecorators = this.$store.getters.getListDetails(this.section);
let url = this.endpoint + '?page=' + listDecorators.page + '&filters=' + JSON.stringify(listDecorators.filters);
this.isLoading = true;
this.submitJob(url);
this.$store.dispatch('submitJobRequest', {'url': url, 'name': this.section});
},
successHandler(response){
let result = JSON.parse(response.payload.data.result);
@@ -118,83 +128,21 @@
to: result.meta.to,
total: result.meta.total
};
this.stopPolling();
this.$store.dispatch('completeList', {'name': this.section, 'data': result.data});
this.$store.dispatch('stopPollingJobResultByJobId', {'jobId': this.getJob.jobId});
this.$refs.pagination.makePagination(result.meta, result.links);
this.isLoading = false;
},
errorHandler(error){
this.isFetchingResult = false;
this.isPolling = false;
// console.log("Error: " + JSON.stringify(error));
this.$store.dispatch('updatePollingJobResultByJobId', {'jobId': this.getJob.jobId, 'isPolling': false, 'isFetchingResult': false });
},
startPolling(jobId, maxAttempts = 8) {
let attempts = 0;
let interval = 10000; // Initial interval
const resetPollingInterval = (customInterval) => {
this.pollingInterval = setInterval(pollJobResult, customInterval);
};
const pollJobResult = () => {
if (this.isPolling || this.isFetchingResult) {
return;
}
this.isPolling = true;
attempts++;
if(attempts === 1){
this.stopPolling();
resetPollingInterval(5000);
}
if (attempts > maxAttempts) {
this.stopPolling();
this.isLoading = false;
console.log(`Reached maximum attempts (${maxAttempts}). Polling stopped.`);
return;
}
if(attempts === maxAttempts){
this.fetchJobResult(jobId, true);
}
else{
this.fetchJobResult(jobId);
}
};
// pollJobResult(); // Initial call
this.pollingInterval = setInterval(pollJobResult, interval);
},
stopPolling() {
clearInterval(this.pollingInterval);
this.pollingInterval = null;
this.isPolling = false;
this.isFetchingResult = false;
},
submitJob(url){
fetchJobResult(isLastAttempt = false) {
this.$store.dispatch('updatePollingJobResultByJobId', {'jobId': this.getJob.jobId, 'isFetchingResult': true });
try {
this.$store.dispatch('crudRequestV2', {endpoint: url, method: 'get'}).then(response => {
let success = response.ok;
response.json().then(response => {
if(!success){return;}
let jobId = response.payload.data.job_id;
if(jobId){
this.startPolling(jobId);
}
});
})
} catch (error) {
console.error('Error submitJob', error);
}
},
fetchJobResult(jobId, isLastAttempt = false) {
// console.log('fetchJobResult: ', jobId);
this.isFetchingResult = true;
try {
let anotherEndpoint = route('api.job.fetch', jobId);
let anotherEndpoint = route('api.job.fetch', this.getJob.jobId);
if(isLastAttempt){
anotherEndpoint = route('api.job.fetch.last.attempt', jobId, isLastAttempt);
anotherEndpoint = route('api.job.fetch.last.attempt', this.getJob.jobId, isLastAttempt);
}
this.poll(anotherEndpoint, 'get', this.section, false, false); //cief todo: Uncaught (in promise) null
} catch (error) {
@@ -5,9 +5,10 @@
<div class="col-6 no-padding">
<div class="row">
<div class="col">
<a :href="route('order.qr.download', item.id)" target="_blank">
<a :href="route('order.qr.download', item.id)" target="_blank" class="d-flex justify-content-center allign-items-center">
<div class="peelable-corner overflow-hidden relative">
<img :src="'https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl={id:'+item.id+'}'" class="w-100">
<!-- <img :src="'https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl={id:'+item.id+'}'" class="w-100"> -->
<img :src="this.$qr_code_img_url + item.id + '}'" class="w-100" style="max-width: 150px; max-height: 150px;">
</div>
</a>
</div>
@@ -47,7 +47,7 @@
</div>
</div>
</div>
<div class="row b-b b-grey p-b-20 align-items-center">
<div class="row b-b b-grey p-b-20 align-items-center parentContainer">
<div class="col">
<div class="row m-b-5">
<div class="col-auto">
@@ -58,9 +58,48 @@
<small class="fs-10 all-caps muted">Status</small>
<p class="no-margin bold">{{item.container ? item.container.transport.schedule_complete && status === 'Shipping' ? 'Custom Clearance' : status : status}}</p>
</div>
<div class="col-auto" v-if="$store.getters.isAdmin">
<small class="fs-10 all-caps muted">Container</small>
<p class="no-margin bold">{{item.container ? item.container.container_reference : '-'}}</p>
<div class="col" v-if="$store.getters.isAdmin">
<div class="row h-100 justify-content-center align-items-center">
<div class="col">
<small class="fs-10 all-caps muted">Container</small>
<p class="no-margin bold">{{ item.container ? item.container.container_reference : '-' }}</p>
</div>
<div class="col-auto" v-if="$store.getters.isAdmin && item.container">
<i class="fa fa-comment fs-15 pointer requestModal" data-type="commentsModal"></i>
<modal-component type="commentsModal" styleType="fill-in">
<div class="row">
<div class="col bg-white b-rad-lg p-t-10 p-t-10 p-l-40 p-r-40">
<h4 class="m-b-20">Remarks</h4>
<div class="row" v-if="item.container.remarks.length">
<div class="col">
<div class="row" v-for="remarks in item.container.remarks">
<div class="col">
<container-comment-component :section="section" :data="remarks"></container-comment-component>
</div>
</div>
</div>
</div>
<div class="row align-items-center justify-content-center p-t-50 p-b-50" v-else>
<div class="col-10">
<div class="row align-items-center justify-content-center hint-text">
<div class="col-4 hint-text"><img src="/images/not-found-illustration.png" class="w-100 hint-text"/></div>
</div>
<div class="row text-center">
<div class="col">
<div class="row m-t-20">
<div class="col">
<p class="all-caps no-margin fs-11" style="letter-spacing: 2px;">Nothing To Show Here Yet!</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</modal-component>
</div>
</div>
</div>
</div>
<div class="row">

Some files were not shown because too many files have changed in this diff Show More