Compare commits

...

409 commits

Author SHA1 Message Date
Ramon Gutierrez
630796df85 Merge branch '811-add-sealant-fee' into 'master'
Resolve "Add sealant fee"

Closes #811

See merge request jankstudio/resq!924
2024-12-08 14:35:58 +00:00
dedfb58a28 Fix issue of sealant and coolant fee not displaying in open edit form. #811 2024-12-05 01:48:39 -05:00
1b24e68f6b Add processing of sealant fee for tire repair service. #811 2024-12-04 02:51:50 -05:00
Ramon Gutierrez
dc4d5f90a7 Merge branch '808-add-form-validation-to-role' into 'master'
Validate role ID and name are not blank #808

Closes #808

See merge request jankstudio/resq!923
2024-10-02 05:58:32 +00:00
Ramon Gutierrez
0179a1b87e Validate role ID and name are not blank #808 2024-10-02 13:58:03 +08:00
Ramon Gutierrez
f1609b2273 Merge branch '807-disable-sms-for-certain-rejection-reasons' into 'master'
Resolve "Disable SMS for certain Rejection Reasons"

Closes #807 and #792

See merge request jankstudio/resq!922
2024-09-24 07:15:30 +00:00
22215ff9f6 Merge branch '807-disable-sms-for-certain-rejection-reasons' of gitlab.com:jankstudio/resq into 807-disable-sms-for-certain-rejection-reasons 2024-09-24 02:47:11 -04:00
Ramon Gutierrez
6994d69020 Merge branch '792-battery-facilitated-by-issue' into 'master'
Resolve "Battery Facilitated By issue"

Closes #792

See merge request jankstudio/resq!903
2024-09-24 02:45:43 -04:00
Ramon Gutierrez
83bdb5c626 Merge branch '792-battery-facilitated-by-issue' into 'master'
Resolve "Battery Facilitated By issue"

Closes #792

See merge request jankstudio/resq!903
2024-09-24 04:40:00 +00:00
Ramon Gutierrez
5a5113b166 Merge branch '803-fix-invoice-data-disappearing-on-open-edit-form' into 'master'
Fix invoice data on open edit form

Closes #803

See merge request jankstudio/resq!918
2024-09-24 04:23:02 +00:00
Ramon Gutierrez
629cc2afa1 Merge branch 'master' into 803-fix-invoice-data-disappearing-on-open-edit-form 2024-07-30 21:27:23 +08:00
Ramon Gutierrez
ad946247ba Merge branch '806-restore-inventory-counters-on-processing-form' into 'master'
Restore inventory retrieval on processing form #806

Closes #806

See merge request jankstudio/resq!921
2024-07-30 13:26:45 +00:00
Ramon Gutierrez
63902e5838 Restore inventory retrieval on processing form #806 2024-07-30 16:39:11 +08:00
Ramon Gutierrez
511aa5ec9b Merge branch 'master' into 803-fix-invoice-data-disappearing-on-open-edit-form 2024-07-18 16:34:59 +08:00
Ramon Gutierrez
2df53674c4 Merge branch '800-automatically-reject-jo-if-no-riders-or-inventory' into 'master'
Add hub selector filtering for available riders, refactor inventory filter to...

Closes #800

See merge request jankstudio/resq!920
2024-07-18 08:34:02 +00:00
Ramon Gutierrez
4ab6b0659d Merge branch 'master' into 800-automatically-reject-jo-if-no-riders-or-inventory 2024-07-18 16:30:10 +08:00
Ramon Gutierrez
a66d16a58b Restored hub filter area functionality #800 2024-07-16 16:10:29 +08:00
Ramon Gutierrez
5f2ed34d8c Disable round robin filter for emergency JOs #800 2024-07-12 14:17:59 +08:00
Ramon Gutierrez
e7d8af516e Temporarily disable hub filter area checks #800 2024-07-09 16:55:58 +08:00
Ramon Gutierrez
8da5381a1c Enable inventory retrieval if inventory checks are disabled #800 2024-07-05 15:54:14 +08:00
Ramon Gutierrez
f8d90cbdcd Fix typo on ResqJobOrderHandler properties #800 2024-07-04 17:06:46 +08:00
Ramon Gutierrez
6139e649fb Add logging for closest hubs selected #800 2024-07-04 16:10:02 +08:00
Ramon Gutierrez
e1103cf108 Move enabled filters list to env #800 2024-07-04 15:41:01 +08:00
Ramon Gutierrez
a10f58e425 Add filtering for JOs with VIP transaction origin #800 2024-07-04 12:57:27 +08:00
Ramon Gutierrez
23d814d1e9 Add extra logs to hub filtering #800 2024-07-03 23:41:44 +08:00
Ramon Gutierrez
9b63139957 Fix file logging format for hub filtering #800 2024-07-03 23:14:38 +08:00
Ramon Gutierrez
a335f84a13 Fix hub filter log location #800 2024-07-03 14:01:16 +08:00
Ramon Gutierrez
8992fdeec0 Add hub rejection log file #800 2024-07-03 13:56:27 +08:00
Ramon Gutierrez
57b4227da9 Add more requested JO rejection reasons #800 2024-06-30 11:47:23 +08:00
Ramon Gutierrez
bff1f7ff73 Add null check to battery inventory entries from motiv #800 2024-06-27 11:31:02 +08:00
Ramon Gutierrez
e8b14ff379 Fix available rider filter to also consider flag_active and current_job_order #800 2024-06-27 10:58:11 +08:00
Ramon Gutierrez
44b3753ece Skip hub filtering if hub list is alraedy empty #800 2024-06-26 10:02:04 +08:00
Ramon Gutierrez
a21ed66490 Move requested params to filters instead of hub selector #800 2024-06-26 09:59:29 +08:00
3fc04cd733 Fix trade-in issue for invoice. #803 2024-06-25 01:44:44 -04:00
Ramon Gutierrez
3b287236ec Add regional filter support for hub filters #800 2024-06-25 03:38:56 +08:00
Ramon Gutierrez
204c039fba Refactor hub selector to separate filters into individual classes #800 2024-06-25 03:19:10 +08:00
Ramon Gutierrez
b4057de938 Add hub filter exceptions to supported areas #800 2024-06-25 03:18:31 +08:00
2a30787dba Fix for issue with customer app JO request with trade in. #803 2024-06-24 06:00:01 -04:00
acee5f8f55 Populate invoice items array upon loading of form. #803 2024-06-21 05:37:04 -04:00
90628f0b54 Add trade in type to invoice item. #03 2024-06-21 05:24:58 -04:00
4ccdd29ae6 Changed the input for trade in items to battery size. #03 2024-06-21 03:39:44 -04:00
2f9ee4ec67 Add battery to criteria for battery replacement warranty. #803 2024-06-20 05:26:23 -04:00
4b5ad97225 Add battery size id to invoice item. Add saving of trade in items with battery size to invoice item. #803 2024-06-20 03:29:24 -04:00
Ramon Gutierrez
e83de164c1 Merge branch '804-add-checking-for-null-user-for-rejection-detail-report' into 'master'
Resolve "Add checking for null user for rejection detail report"

Closes #804

See merge request jankstudio/resq!917
2024-06-10 10:40:38 +00:00
8c886c4dbf Add check for null for user for hub rejection. #804 2024-06-10 02:20:28 -04:00
Ramon Gutierrez
10c44fbe64 Limit hub rejection SMS to one per JO/hub combo #800 2024-06-07 04:19:08 +08:00
Ramon Gutierrez
b5f169c14e Merge branch 'master' into 800-automatically-reject-jo-if-no-riders-or-inventory 2024-06-06 05:52:03 +08:00
Ramon Gutierrez
cec648f894 Merge branch '801-handle-failed-paymongo-payments' into 'master'
Attempt to re-enable paymongo webhook if found disabled #801

Closes #801

See merge request jankstudio/resq!916
2024-06-05 21:51:18 +00:00
Ramon Gutierrez
c4b0513386 Attempt to re-enable paymongo webhook if found disabled #801 2024-06-06 05:50:36 +08:00
Ramon Gutierrez
90838b004a Merge branch 'master' into 800-automatically-reject-jo-if-no-riders-or-inventory 2024-06-04 17:49:39 +08:00
Ramon Gutierrez
c60a39c13e Merge branch '802-fix-jos-being-able-to-proceed-after-cancellation' into 'master'
Add checks to rider app API for JO status and delivery status #802

Closes #802

See merge request jankstudio/resq!915
2024-06-04 09:47:46 +00:00
Ramon Gutierrez
792db080a1 Merge branch '801-handle-failed-paymongo-payments' into 'master'
Resolve "Handle failed PayMongo payments"

Closes #801

See merge request jankstudio/resq!914
2024-06-04 09:45:37 +00:00
Ramon Gutierrez
08084f682c Add force option to enable or disable webhook status checking, defaults to false #801 2024-05-31 18:51:55 +08:00
Ramon Gutierrez
3846ad5a43 Add checker for paymongo webhook status before running manual processing command #801 2024-05-31 18:45:16 +08:00
Ramon Gutierrez
191a02f4c4 Fix typo on getStatus call on entity listener #801 2024-05-31 12:28:43 +08:00
Ramon Gutierrez
e97cebd2b2 Fix typo in log message for new command #801 2024-05-31 12:11:13 +08:00
Ramon Gutierrez
952122a39e Prevent insurance applications from being flagged as paid more than once #801 2024-05-31 06:50:34 +08:00
Ramon Gutierrez
ad841a7e25 Prevent success handler from catching failed paymongo payment attempts #801 2024-05-31 06:49:55 +08:00
Ramon Gutierrez
75b2ada03f Add command for processing late pending paymongo transactions #801 2024-05-31 06:48:44 +08:00
Ramon Gutierrez
4b19cff996 Add checks to rider app API for JO status and delivery status #802 2024-05-30 17:15:47 +08:00
Ramon Gutierrez
2ebb6e040a Add placeholder debug logs #800 2024-05-30 16:06:55 +08:00
Ramon Gutierrez
404401d854 Fix erroneous delivery status IDs on collection #800 2024-05-30 16:06:04 +08:00
Ramon Gutierrez
6dfaeee799 Generate JO ID before hub selection on user app API, add additional params to HubCriteria for JORejection and SMS #800 2024-05-20 03:46:08 +08:00
Ramon Gutierrez
aae4aaa390 Enable inventory and rider hub filters for emergency JOs, disable for VIP customers #800 2024-05-18 15:21:32 +08:00
Ramon Gutierrez
b53aacb840 Update rejection messages to use battery name and model vs SKUs #800 2024-05-17 22:23:26 +08:00
Ramon Gutierrez
22683e1edf Add null check to user on detailed rejection report #800 2024-05-17 22:01:43 +08:00
Ramon Gutierrez
4be9134090 Update SMS message format for rejected hubs, add JO rejection entries for no inventory or no riders #800 2024-05-17 21:40:16 +08:00
Ramon Gutierrez
21c97df677 Set JO type regardless of area support on hub form initialization to trigger inventory filter #800 2024-05-16 21:46:02 +08:00
Ramon Gutierrez
5cf2c3619f Remove debug code from JO handler #800 2024-05-16 15:07:35 +08:00
Ramon Gutierrez
7b6afbb099 Modify inventory SMS message #800 2024-05-15 18:00:27 +08:00
Ramon Gutierrez
190ac88153 Enable inventory and rider filtering on CRM JO form hub lists #800 2024-05-15 18:00:07 +08:00
Ramon Gutierrez
aed31f2a33 Rearrange flow logic on hub selector, save inventory counts upon retrieval if enabled #800 2024-05-15 17:59:27 +08:00
Ramon Gutierrez
76496ed6fe Fix issues with inventory filter flow #800 2024-05-14 17:51:00 +08:00
Ramon Gutierrez
89e5dd799f Update hub rejection SMS translations #800 2024-05-14 17:49:34 +08:00
Ramon Gutierrez
0bd6a89840 Fix missing keys on NameValue class #800 2024-05-14 17:49:14 +08:00
Ramon Gutierrez
be0e69db89 Add blacklist support to base NameValue class, for hiding options when getting entire collection #800 2024-05-14 14:45:51 +08:00
Ramon Gutierrez
d52402a2ef Enable inventory and rider checks on hub criteria on user app and TAPI #800 2024-05-14 04:18:37 +08:00
Ramon Gutierrez
a45c3dd65c Add hub selector filtering for available riders, refactor inventory filter to send a single batch call instead #800 2024-05-14 03:39:45 +08:00
Ramon Gutierrez
b19d9c203a Merge branch '783-rider-app-trade-in-support' into 'master'
Resolve "Rider app trade-in support"

Closes #783

See merge request jankstudio/resq!912
2024-04-23 13:11:51 +00:00
Ramon Gutierrez
a64557ffcd Fix API roles permissions list to properly display the correct ACL group #783 2024-04-06 14:56:16 +08:00
Ramon Gutierrez
d7909f7941 Merge branch '796-price-display-issue-with-price-tier' into '783-rider-app-trade-in-support'
Resolve "Price display issue with price tier"

See merge request jankstudio/resq!908
2024-04-05 10:59:32 +00:00
Ramon Gutierrez
8e383fd23f Merge branch '794-capi-calls-for-pullout-form-system' into '783-rider-app-trade-in-support'
Resolve "CAPI calls for Pullout Form System"

See merge request jankstudio/resq!906
2024-04-05 10:57:13 +00:00
Ramon Gutierrez
86889b3147 Merge branch '798-motolite-user-jumpstart-fee' into '783-rider-app-trade-in-support'
Resolve "Motolite user jumpstart fee"

See merge request jankstudio/resq!910
2024-04-02 21:08:35 +00:00
6a804c11df Add sql file to update service offering table. #798 2024-04-02 01:21:23 -04:00
c8e2c02be1 Add a separate service fee for motolite users for jumpstart. #798 2024-04-02 01:11:32 -04:00
Ramon Gutierrez
06dc8eae7b Merge branch '795-jumpstart-service-fee-for-motolite-users' into '783-rider-app-trade-in-support'
Resolve "Jumpstart service fee for motolite users"

See merge request jankstudio/resq!907
2024-04-01 13:19:07 +00:00
Ramon Gutierrez
f97f287f5b Merge branch '797-resq-dispatch-display-issue' into '783-rider-app-trade-in-support'
Resolve "Resq Dispatch Display Issue"

See merge request jankstudio/resq!909
2024-04-01 13:17:25 +00:00
6f2fca292e Add use statement. #797 2024-04-01 06:55:23 -04:00
b80ece9084 Fix display issue for invoice. #796 2024-03-26 01:41:48 -04:00
90aada4bf3 ADd service offering for motolite users for jumpstart warranty. Add sql file to add new service offering. #795 2024-03-25 04:51:40 -04:00
93c313f111 Fix issues found during testing. #794 2024-03-24 23:38:08 -04:00
Ramon Gutierrez
25e0931f6f Revert warranty serial check for now #783 2024-03-23 04:03:00 +08:00
Ramon Gutierrez
5af3a3cb5e Add check for if serial is present before registering warranty #783 2024-03-23 04:01:16 +08:00
bf0d1f664b Add CAPI calls for hub list and job order details. #794 2024-03-22 05:35:06 -04:00
Ramon Gutierrez
07b459e7a3 Add pycache and test insurance banner to gitignore #783 2024-03-19 19:16:34 +08:00
Ramon Gutierrez
c4e03f861d Exclude cancelled JOs on rider app API #783 2024-03-19 17:33:13 +08:00
Ramon Gutierrez
cd21e41d2f Merge branch '793-prevent-jo-progression-on-rider-app-after-cancellation' into '783-rider-app-trade-in-support'
Rename JO progression check method #793

See merge request jankstudio/resq!905
2024-03-19 08:36:23 +00:00
Ramon Gutierrez
1a6af00399 Rename JO progression check method #793 2024-03-19 16:35:51 +08:00
Ramon Gutierrez
c03025748f Merge branch '793-prevent-jo-progression-on-rider-app-after-cancellation' into '783-rider-app-trade-in-support'
Resolve "Prevent JO progression on rider app after cancellation"

See merge request jankstudio/resq!904
2024-03-19 08:33:43 +00:00
Ramon Gutierrez
ca513355fe Switch parameters of allowJOProgress to fix error #793 2024-03-19 15:25:32 +08:00
Ramon Gutierrez
9152370300 Add checks to prevent JO progression if cancelled #793 2024-03-19 15:22:34 +08:00
Ramon Gutierrez
bff89a6817 Add regional pricing support to job order updates on rider app #783 2024-03-15 16:24:09 +08:00
Ramon Gutierrez
a59aa0f66d Make insurance body types a namevalue class #783 2024-03-14 13:40:37 +08:00
Ramon Gutierrez
69218aecf4 Update insurance application paid call to use external transaction id #783 2024-03-13 14:33:29 +08:00
Ramon Gutierrez
47dcd92474 Refactor insurance, paymongo connector logging logic #783 2024-03-13 07:09:56 +08:00
Ramon Gutierrez
9f4c16b149 Add debug code for insurance api #783 2024-03-12 16:34:21 +08:00
Ramon Gutierrez
175ac92765 Fix syntax error on rider api controller #783 2024-03-11 23:50:44 +08:00
Ramon Gutierrez
801a274e8c Disable serial number requirement on rider api to allow old rider app to work for now #783 2024-03-11 23:45:05 +08:00
Ramon Gutierrez
d603934d93 Update the correct MQTT client with latest changes #783 2024-03-07 23:41:21 +08:00
Ramon Gutierrez
a4b883b7ea Fix rider assign MQTT event for new rider app #783 2024-03-07 23:39:57 +08:00
Ramon Gutierrez
a2cd86b48c Merge branch '780-regional-pricing' into 783-rider-app-trade-in-support
# Conflicts:
#	src/Controller/CAPI/RiderAppController.php
#	src/InvoiceRule/BatterySales.php
2024-03-07 09:42:42 +08:00
5ddebcd95f Add saving of facilitated hub data when reassigning hub. Fix display of dropdown for facilitated hubs. #792 2024-03-04 22:45:57 -05:00
Ramon Gutierrez
ef5e629358 Merge branch '791-support-for-insurance-premiums-banner' into 780-regional-pricing 2024-02-23 18:40:10 +08:00
Ramon Gutierrez
4dd8efd95a Fix returned format of body types endpoint #791 2024-02-23 18:39:21 +08:00
34adefb798 Merge branch '791-support-for-insurance-premiums-banner' of gitlab.com:jankstudio/resq into 780-regional-pricing 2024-02-23 04:01:58 -05:00
Ramon Gutierrez
78a43ae85c Add insurance body types endpoint #791 2024-02-23 16:53:04 +08:00
7ed9f90945 Merge branch '791-support-for-insurance-premiums-banner' of gitlab.com:jankstudio/resq into 780-regional-pricing 2024-02-22 19:42:15 -05:00
Ramon Gutierrez
0a4f78559c Merge branch '791-support-for-insurance-premiums-banner' into 783-rider-app-trade-in-support 2024-02-22 16:51:55 +08:00
Ramon Gutierrez
270a4cfb10 Add endpoint for insurance premiums banner #791 2024-02-22 16:51:26 +08:00
Ramon Gutierrez
7a5583d840 Include trade in type and container size to battery info rider api call #783 2024-02-22 16:08:36 +08:00
4d89e7420f Add checking for location and price tier when getting list of compatible batteries. #780 2024-02-20 02:09:57 -05:00
010bdca458 Add updating of other fields when updating job order. Fix issue found during testing in invoice rule. #783 2024-02-12 06:19:00 -05:00
abf4bbfe22 Add checking for promo. #783 2024-02-12 03:46:26 -05:00
Ramon Gutierrez
eebd1d93c4 Fix battery info endpoint to work with warranty serials instead #783 2024-02-08 23:17:32 +08:00
Ramon Gutierrez
ba09e6ac7b Merge branch '782-invoice-and-jo-modifications-for-regional-pricing' into '780-regional-pricing'
Resolve "Invoice and JO modifications for regional pricing"

See merge request jankstudio/resq!894
2024-02-08 06:21:40 +00:00
Ramon Gutierrez
8860796db2 Add endpoint for rider app trade-in types #783 2024-02-07 15:02:47 +08:00
Ramon Gutierrez
3254527653 Merge branch 'master' into '782-invoice-and-jo-modifications-for-regional-pricing'
Master

See merge request jankstudio/resq!901
2024-02-06 08:16:28 +00:00
8ca7292a25 Add processing of trade in items from rider app. #783 2024-02-05 03:06:06 -05:00
c9057b9617 Add API call to update invoice. #783 2024-02-02 04:50:03 -05:00
86744afde3 Add serial as a parameter when rider fulfills a job order. #783 2024-02-01 04:24:09 -05:00
ae46d64f5b Add API call to return battery data given a serial. #783 2024-02-01 03:10:16 -05:00
Ramon Gutierrez
a5340fbfd6 Merge branch '788-remove-geofence-message-from-view-jo' into 'master'
Resolve "Remove geofence message from View JO"

Closes #788

See merge request jankstudio/resq!900
2024-02-01 06:46:29 +00:00
0e5365a015 Comment out geofence message when viewing JO. Initialize branch_codes array. #788 2024-01-31 20:13:41 -05:00
Ramon Gutierrez
313dc74eb0 Merge branch '784-transaction-origin-additional-fields' into 'master'
Fix transaction origin list to replace existing facebook and hotline with manila entries #784

Closes #784

See merge request jankstudio/resq!899
2024-01-31 14:06:05 +00:00
Ramon Gutierrez
33eaf9bbff Fix transaction origin list to replace existing facebook and hotline with manila entries #784 2024-01-31 22:05:42 +08:00
Ramon Gutierrez
8ee1809a99 Merge branch '787-update-geofence-message-in-app' into 'master'
Resolve "Update geofence message in app"

Closes #787

See merge request jankstudio/resq!898
2024-01-30 07:25:15 +00:00
5717eddaa0 Update geofence message in the app. #787 2024-01-30 02:24:08 -05:00
Ramon Gutierrez
35b3b23e55 Merge branch '786-remove-geofence-message-in-crm' into 'master'
Resolve "Remove geofence message in CRM"

Closes #786

See merge request jankstudio/resq!897
2024-01-30 07:14:34 +00:00
f7c8034538 Comment out the geofence call for admin panel. #786 2024-01-30 02:12:08 -05:00
Ramon Gutierrez
ba8ac73c9d Merge branch '785-no-sms-notification-for-discount' into 'master'
Resolve "No SMS notification for discount"

Closes #785

See merge request jankstudio/resq!896
2024-01-30 07:03:47 +00:00
9b52d4578f Add checking for discount rejection reason. #785 2024-01-30 02:02:24 -05:00
Ramon Gutierrez
b033e5674d Merge branch '784-transaction-origin-additional-fields' into 'master'
Resolve "Transaction Origin additional fields"

Closes #784

See merge request jankstudio/resq!895
2024-01-30 06:33:05 +00:00
0e2afaa400 Add new fields for transaction origin. #784 2024-01-30 01:30:09 -05:00
Ramon Gutierrez
b123be25cc Add battery sizes endpoint to rider api #783 2024-01-30 03:42:05 +08:00
d4eae00902 Add sql for item types. #782 2024-01-28 22:21:34 -05:00
20f5bb08e0 Add checking for longitude and latitude when calling getEstimate. #782 2024-01-26 04:50:54 -05:00
213171f4b7 Add price tiering for Resq 2 App and TAPI. #782 2024-01-26 04:44:18 -05:00
c136b0666b Fix issues found during testing. #782 2024-01-25 03:08:00 -05:00
57fd7fe5ac Fix issues found during testing. #782 2024-01-25 02:23:47 -05:00
bc6364ace5 Add price tier to invoice rules. #782 2024-01-25 02:11:05 -05:00
c5b395d720 Add price tier for battery replacement warranty. #782 2024-01-24 04:02:14 -05:00
b6763bfd3e Add price tier checking for battery sales. #782 2024-01-24 02:24:13 -05:00
29ad8d57a4 Add processing of battery entries and invoice item titles. #782 2024-01-22 04:24:41 -05:00
root
70ee7fdd89 Add invoice rule for price tier. #782 2024-01-19 04:14:06 -05:00
Korina Cordero
ee033ddd55 Fix issues found during testing. #780 2024-01-17 15:31:26 +08:00
Korina Cordero
a83fecf224 Merge branch 'master' of gitlab.com:jankstudio/resq into 780-regional-pricing 2024-01-17 14:50:50 +08:00
Korina Cordero
9de6fa7999 Fix issues found during saving of item prices. #780 2024-01-17 14:25:28 +08:00
Korina Cordero
6d7c8c5b53 Add saving of item prices for price tier. #780 2024-01-16 16:28:26 +08:00
Korina Cordero
022336ad8f Add saving of prices. #780 2024-01-15 17:21:31 +08:00
Korina Cordero
bfe7a5fbf6 Fix display of item prices per tier. #780 2024-01-15 15:23:07 +08:00
Korina Cordero
b592390554 Add response when displaying item prices. #780 2024-01-11 16:48:23 +08:00
Korina Cordero
7f4675a8a2 Add item type dropdown to form. #780 2024-01-10 17:23:15 +08:00
Korina Cordero
f65ca19010 Load service offering into Item Pricing page. #780 2024-01-09 17:39:29 +08:00
c17be92f0a Fix errors for item query. #780 2023-12-31 00:46:12 -05:00
80b9f90324 Rename ItemPriceController to ItemController. #780 2023-12-30 23:09:27 -05:00
01e4baa8c4 Add item price controller. #780 2023-12-28 20:41:44 -05:00
e4ffcc0c9d Add controller for item pricing. #780 2023-12-27 22:15:44 -05:00
Ramon Gutierrez
9c081b416e Merge branch '781-fix-character-length-for-motolite-events-title' into 'master'
Increase motolite event name field size to 255 #781

Closes #781

See merge request jankstudio/resq!893
2023-12-22 07:30:47 +00:00
Ramon Gutierrez
5ee41dab32 Increase motolite event name field size to 255 #781 2023-12-22 15:30:11 +08:00
root
58f46fd5bf Add route and CRUD for item type. #780 2023-12-21 23:23:46 -05:00
Korina Cordero
8c810bf27a Add validation and deletion for price tier. #780 2023-12-21 16:24:37 +08:00
Korina Cordero
fa3cf12be1 Add controller for price tier. #780 2023-12-20 18:15:17 +08:00
Korina Cordero
9447f64312 Create item price, price tier, and item type entities for regional pricing. #780 2023-12-19 17:30:38 +08:00
Ramon Gutierrez
b51160e9d0 Merge branch '779-fix-battery-recommendations-still-including-inactive-batteries' into 'master'
Display inactive batteries on edit vehicle form, exclude inactive batteries on...

Closes #779

See merge request jankstudio/resq!891
2023-12-18 09:18:17 +00:00
Ramon Gutierrez
d8bd2803e0 Display inactive batteries on edit vehicle form, exclude inactive batteries on battery recommendations API endpoint #779 2023-12-18 17:17:40 +08:00
Ramon Gutierrez
a7c7b03d06 Merge branch '746-resq-2-0-final' into 'master'
Add source to invoice criteria. Modify invoice rules to get service fees from...

See merge request jankstudio/resq!890
2023-11-22 08:54:47 +00:00
Ramon Gutierrez
ebfa3495a7 Merge branch '778-add-in-app-support-for-battery-trade-ins' into '746-resq-2-0-final'
Resolve "Add in-app support for battery trade-ins"

See merge request jankstudio/resq!889
2023-11-22 08:50:51 +00:00
Ramon Gutierrez
7c09dcac65 Merge branch '746-resq-2-0-final' into 778-add-in-app-support-for-battery-trade-ins
# Conflicts:
#	config/routes/apiv2.yaml
#	src/Controller/CustomerAppAPI/VehicleController.php
2023-11-18 04:36:57 +08:00
Ramon Gutierrez
fb2ca0bbd8 Merge branch '761-add-paramount-insurance-and-paymongo-support-for-resq2-api' into '746-resq-2-0-final'
Resolve "Add Paramount insurance and PayMongo support for RESQ2 API"

See merge request jankstudio/resq!888
2023-11-17 20:28:02 +00:00
Ramon Gutierrez
9bc9b13055 Merge branch '765-change-fax-to-alternative-phone-number' into '746-resq-2-0-final'
Resolve "Change Fax to Alternative Phone Number"

See merge request jankstudio/resq!879
2023-11-17 08:43:21 +00:00
Ramon Gutierrez
4773d6c08c Add trade-in support to invoice generation and JO creation for app #778 2023-11-17 06:30:07 +08:00
Ramon Gutierrez
fd0c9ede26 Merge branch '746-resq-2-0-final' into 778-add-in-app-support-for-battery-trade-ins 2023-11-14 19:25:54 +08:00
Ramon Gutierrez
ef10f7c7b9 Add endpoint for getting estimated trade-in value #778 2023-11-14 19:25:05 +08:00
Ramon Gutierrez
6d4614f634 Merge branch '772-remove-list-of-car-club-members-checkboxes-from-jo' into '746-resq-2-0-final'
Resolve "Remove list of car club members checkboxes from JO"

See merge request jankstudio/resq!886
2023-11-14 07:18:19 +00:00
Korina Cordero
cf5da6ea7b Comment out customer car club code. #772 2023-11-14 14:56:32 +08:00
Ramon Gutierrez
2e5ce7e28b Merge branch '770-add-another-module-for-all-resq-transactions' into '746-resq-2-0-final'
Resolve "Add another module for all RESQ transactions"

See merge request jankstudio/resq!885
2023-11-14 04:49:04 +00:00
Korina Cordero
deb26d3aa8 Remove debug message. #770 2023-11-13 18:30:07 +08:00
Korina Cordero
1dbfd2e3d1 Rename the menu item for Resq view all. #770 2023-11-13 18:29:24 +08:00
Korina Cordero
6cf94bb193 Add the new columns to the Resq View All page. #770 2023-11-13 18:27:01 +08:00
Korina Cordero
14d62d044f Make new page to view all Resq app job orders. #770 2023-11-13 15:20:46 +08:00
Ramon Gutierrez
fba077d4d3 Merge branch '773-add-a-dropdown-box-in-customer-information-to-indicate-if-customer-is-new-old' into '746-resq-2-0-final'
Resolve "Add a dropdown box in Customer Information to indicate if customer is new/old"

See merge request jankstudio/resq!883
2023-11-10 05:47:05 +00:00
Korina Cordero
c7b2502f0a Merge branch '746-resq-2-0-final' of gitlab.com:jankstudio/resq into 773-add-a-dropdown-box-in-customer-information-to-indicate-if-customer-is-new-old 2023-11-10 13:46:07 +08:00
Ramon Gutierrez
0ded3cea83 Merge branch '767-enable-viewing-of-jos-locked-to-another-agent' into '746-resq-2-0-final'
Resolve "Enable viewing of JOs locked to another agent"

See merge request jankstudio/resq!880
2023-11-10 05:45:32 +00:00
Korina Cordero
1ce284578d Merge branch '746-resq-2-0-final' of gitlab.com:jankstudio/resq into 767-enable-viewing-of-jos-locked-to-another-agent 2023-11-10 13:40:52 +08:00
Korina Cordero
24332c0f9b Merge branch '746-resq-2-0-final' of gitlab.com:jankstudio/resq into 773-add-a-dropdown-box-in-customer-information-to-indicate-if-customer-is-new-old 2023-11-10 13:39:12 +08:00
Korina Cordero
91edfa6ef0 Fix issues found during testing. #773 2023-11-10 13:35:49 +08:00
Korina Cordero
005f891057 Fix issues found during testing. #773 2023-11-10 13:29:52 +08:00
Korina Cordero
c77591fe01 Fix issues during testing. #773 2023-11-09 18:08:59 +08:00
Korina Cordero
fd28be75d1 Add command to update flag_cust_new for existing job orders. Rename flag for consistency. #773 2023-11-09 18:03:17 +08:00
Korina Cordero
806418db52 Fix checking for customer job orders. #773 2023-11-09 16:30:57 +08:00
Korina Cordero
35456cfb19 Add new field for job order for new customers. Add saving of new field for job orders from admin panel and third party API. #773 2023-11-09 12:07:26 +08:00
Ramon Gutierrez
58c3d1c64a Merge branch '768-add-timer-in-jo-list-to-show-real-time-dispatch-time' into '746-resq-2-0-final'
Add dispatch timer to list of all JOs view #768

See merge request jankstudio/resq!884
2023-11-08 22:17:00 +00:00
Ramon Gutierrez
e070d6df18 Merge branch '746-resq-2-0-final' into 768-add-timer-in-jo-list-to-show-real-time-dispatch-time
# Conflicts:
#	templates/job-order/list.all.html.twig
2023-11-09 06:16:24 +08:00
Ramon Gutierrez
ac5683f7f1 Add dispatch timer to list of all JOs view #768 2023-11-09 06:10:51 +08:00
Ramon Gutierrez
0ba63cc106 Merge branch '769-add-transaction-type-for-view-all-header' into '746-resq-2-0-final'
Resolve "Add Transaction Type for View All header"

See merge request jankstudio/resq!882
2023-11-08 07:19:54 +00:00
Korina Cordero
8ed1434353 Add service type to View All page. Replace Battery Facilitated By to Assigned Hub in View All Page. #769 2023-11-08 15:17:32 +08:00
Ramon Gutierrez
b20371cd1f Merge branch '774-add-late-documentation-tickbox-and-reason-field-in-new-ticket-ticket-creation' into '746-resq-2-0-final'
Add source to invoice criteria. Modify invoice rules to get service fees from...

See merge request jankstudio/resq!881
2023-11-06 20:04:50 +00:00
Ramon Gutierrez
2344ec716b Add documentation time column to ticket list #774 2023-11-07 04:02:57 +08:00
Ramon Gutierrez
d44323c097 Add late documentation fields to ticket form #774 2023-11-07 04:02:33 +08:00
Ramon Gutierrez
561a280647 Add late documentation fields to ticket table #774 2023-11-07 03:34:56 +08:00
Korina Cordero
20a6029671 Add view only form for job order. #767 2023-10-27 17:53:10 +08:00
Ramon Gutierrez
ff753d5452 Remove initial 0 from mobile number passed to paymongo checkout api #761 2023-10-23 12:42:09 +08:00
Ramon Gutierrez
2432a08c4d Add support for transient status between making a payment and receiving the webhook #761 2023-10-23 01:17:05 +08:00
Korina Cordero
a29c31d3de Change Fax to Alternative Phone. #765 2023-10-20 13:45:42 +08:00
Ramon Gutierrez
b1ff62d9ec Add back insurance status to vehicle list so latest status can be displayed #761 2023-10-17 14:43:06 +08:00
Ramon Gutierrez
ac2cf864f2 Handle expiry date from insurance completed callback #761 2023-10-13 17:38:03 +08:00
Ramon Gutierrez
708e9a67cc Update insurance callback handler to expect formdata #761 2023-10-13 14:13:57 +08:00
Ramon Gutierrez
38262151e8 Add dates to vehicle info insurance info #761 2023-10-11 17:06:37 +08:00
Ramon Gutierrez
502e9a11cd Add customer vehicle id to insurance FCM notifications #761 2023-10-11 15:39:07 +08:00
Ramon Gutierrez
da87b386a3 Add changelog to insurance info on vehicle endpoint #761 2023-10-09 17:02:23 +08:00
Ramon Gutierrez
d3054760d3 Add external transaction id to vehicle info endpoint #761 2023-10-09 16:56:41 +08:00
Ramon Gutierrez
c332757a53 Fix paymongo handler not checking for the correct payload parameter on validation #761 2023-10-09 16:35:06 +08:00
Ramon Gutierrez
516fbcfcba Add missing dependency to insurance callback handler #761 2023-10-09 15:51:33 +08:00
Ramon Gutierrez
c0c6fe26f7 Fix wrong parameter being checked for invalid insurance callback #761 2023-10-09 15:50:28 +08:00
Ramon Gutierrez
cbe84d1cc8 Set insurance callback handler to process raw json body #761 2023-10-09 15:48:05 +08:00
Ramon Gutierrez
e3a79361bd Remove auth from insurance listener. Fail silently if invalid callback given #761 2023-10-09 14:05:25 +08:00
Ramon Gutierrez
9b616cf398 Handle insurance callbacks, add support for non-JO related FCM notifications #761 2023-10-09 01:15:12 +08:00
Ramon Gutierrez
dd9c9dd0ec Add endpoint for getting individual vehicle info #761 2023-09-26 04:53:13 +08:00
Ramon Gutierrez
38023bdb00 Add insurance information to customer vehicle endpoint #761 2023-09-20 20:40:00 +08:00
Ramon Gutierrez
a002da6aad Add entity null check for paymongo webhooks #761 2023-09-20 06:14:22 +08:00
Ramon Gutierrez
ee021da453 Handle paymongo webhooks #761 2023-09-20 06:08:00 +08:00
Ramon Gutierrez
78956de357 Merge branch '763-add-shopify-to-transaction-origin' into '746-resq-2-0-final'
Resolve "Add Shopify to Transaction Origin"

See merge request jankstudio/resq!877
2023-09-12 13:02:19 +00:00
Ramon Gutierrez
c5a8bda95a Return premium amount on insurance application endpoint #761 2023-09-09 00:42:42 +08:00
Korina Cordero
4dbd2377f4 Add Shopify to TransactionOrigin. #763 2023-09-08 15:31:58 +08:00
Ramon Gutierrez
95f771c252 Update field requirements for is_public and vehicle_use_type #761 2023-09-07 06:54:30 +08:00
Ramon Gutierrez
db75e7becd Add insurance endpoints, insurance and paymongo connectors #761 2023-09-06 04:05:15 +08:00
Ramon Gutierrez
8c11ec9e8f
Merge branch '759-add-inventory-column-to-job-order-details-report' into '746-resq-2-0-final'
Resolve "Add inventory column to Job Order Details Report"

See merge request jankstudio/resq!873
2023-09-05 20:00:36 +00:00
Ramon Gutierrez
f94cec4bfa
Merge branch '760-jumpstart-fee-for-hotline' into '746-resq-2-0-final'
Resolve "Jumpstart fee for Hotline"

See merge request jankstudio/resq!875
2023-09-04 07:58:51 +00:00
Korina Cordero
041248ae0c Set jumpstart fee for motolite users, depending on JO source. #760 2023-09-04 15:36:44 +08:00
Korina Cordero
d00b211363 Merge branch '746-resq-2-0-final' of gitlab.com:jankstudio/resq into 759-add-inventory-column-to-job-order-details-report 2023-08-29 10:15:09 +08:00
Ramon Gutierrez
32f3f957be Merge branch '758-make-service-fees-dynamic' into '746-resq-2-0-final'
Fix issue of jumpstart for motolite user. #758

See merge request jankstudio/resq!874
2023-08-25 06:00:04 +00:00
Korina Cordero
fc95f781d8 Fix issue of jumpstart for motolite user. #758 2023-08-25 09:42:42 +08:00
Korina Cordero
f98b3e170c Merge branch '746-resq-2-0-final' of gitlab.com:jankstudio/resq into 759-add-inventory-column-to-job-order-details-report 2023-08-25 00:25:28 +00:00
Ramon Gutierrez
555e79ab68 Merge branch '758-make-service-fees-dynamic' into '746-resq-2-0-final'
Resolve "Make service fees dynamic"

See merge request jankstudio/resq!872
2023-08-24 10:02:59 +00:00
Korina Cordero
80c762d463 Add inventory to the Job Order Details report. #759 2023-08-24 09:04:40 +00:00
Korina Cordero
4f13114649 Add inventory count to job order. Add saving of inventory count when assigning hub. #759 2023-08-24 07:29:06 +00:00
Korina Cordero
cfa77bf2e1 Add source to invoice criteria. Modify invoice rules to get service fees from database. Modify call to invoice to include source. #758 2023-08-23 07:51:43 +00:00
Korina Cordero
e9631b437f Add sql file to populate the ServiceOffering table. #758 2023-08-22 10:53:18 +00:00
Korina Cordero
3af04a922a Fix input type for fee. #758 2023-08-22 10:34:01 +00:00
Korina Cordero
8f196000a8 Add permisssions, menu, forms, entity, for ServiceOffering. #758 2023-08-22 08:36:51 +00:00
Ramon Gutierrez
236819d951 Merge branch '757-fix-display-of-database-menu-items' into '746-resq-2-0-final'
Resolve "Fix display of Database menu items"

See merge request jankstudio/resq!871
2023-08-18 09:07:50 +00:00
Korina Cordero
70047d4ea0 Resolve "Fix display of Database menu items" 2023-08-18 09:07:50 +00:00
Ramon Gutierrez
f481af3694 Merge branch '756-update-jumpstart-service-fee' into '746-resq-2-0-final'
Resolve "Update jumpstart service fee"

See merge request jankstudio/resq!870
2023-08-16 07:55:14 +00:00
Korina Cordero
acf7ce0d4d Update the service fee for jumpstart. #756 2023-08-16 07:51:53 +00:00
Ramon Gutierrez
de5f55ac0e Use friendlier error message for invalid confirmation code #746 2023-08-14 07:22:41 +08:00
Ramon Gutierrez
3137d39a9a Add customer account validation and response spoofing to account deletion resend code method #746 2023-08-14 05:22:26 +08:00
Ramon Gutierrez
b3b81e4e27 Add transparent check if number is valid before storing new deletion request #746 2023-08-14 00:28:27 +08:00
Ramon Gutierrez
18c295f2b1 Fix customer record association when submitting a delete request #746 2023-08-13 17:36:33 +08:00
Ramon Gutierrez
9ad1f0d9b2 Add account deletion api endpoints #746 2023-08-11 07:26:49 +08:00
Ramon Gutierrez
73738b7a18 Fix checking of test app store numbers #746 2023-08-10 12:49:06 +08:00
Ramon Gutierrez
e3af21f741 Add mfg id to exported vehicle list, fix default sorting of vehicle results #746 2023-08-09 06:01:24 +08:00
Ramon Gutierrez
4516566d56 Add vehicle make and manufacturer CSV export functionality #746 2023-08-09 05:49:26 +08:00
Ramon Gutierrez
26824713da Remove authentication from version check endpoint #746 2023-08-04 18:00:35 +08:00
Ramon Gutierrez
f3992f05fc Update apiv2 app version check to include OS #746 2023-08-02 16:39:30 +08:00
Ramon Gutierrez
e212e2d29a Merge branch '755-resq-2-fix-display-of-discount-type' into '746-resq-2-0-final'
Resolve "RESQ 2: Fix display of discount type"

See merge request jankstudio/resq!869
2023-07-28 09:27:23 +00:00
Korina Cordero
458a34c6a8 Fix setting of promo for invoice. #755 2023-07-28 05:58:03 +00:00
Ramon Gutierrez
1704235bbd Add image for premium excel battery #746 2023-07-26 17:05:50 +08:00
Ramon Gutierrez
0708ebdca9 Fix vertical centering on battery images #746 2023-07-24 16:01:31 +08:00
Ramon Gutierrez
00eb5d1a9d Fix aspect ratio of stock battery images #746 2023-07-24 15:46:12 +08:00
Ramon Gutierrez
570d405347 Update temp static battery images with new ones, handle spaces in image path #746 2023-07-24 15:36:49 +08:00
Ramon Gutierrez
18c893cc6e Merge branch '753-resq-2-set-invoice-criteria-correctly-in-getestimate' into '746-resq-2-0-final'
Resolve "RESQ 2: Set invoice criteria correctly in getEstimate"

See merge request jankstudio/resq!868
2023-07-21 06:34:40 +00:00
Korina Cordero
7b16738709 Fix typo. #753 2023-07-21 06:28:40 +00:00
Korina Cordero
da419dd257 Fix setting of invoice criteria for vat computations and battery entries. #753 2023-07-21 06:25:48 +00:00
Ramon Gutierrez
49ff4b604b Merge branch '752-resq-2-fix-return-values-for-invoice-data' into '746-resq-2-0-final'
Resolve "RESQ 2: Fix return values for invoice data"

See merge request jankstudio/resq!867
2023-07-20 02:30:51 +00:00
Korina Cordero
fc33668a46 Typecast the invoice fields to float. Fix the namespace for APIResponse for TAPI controllers. #752 2023-07-20 02:19:13 +00:00
Ramon Gutierrez
9b75993d3d Merge branch '751-resq-2-test-fix-for-fcm-notification-priority' into '746-resq-2-0-final'
Change dependency for fcm to forked lib with high priority set #751

See merge request jankstudio/resq!866
2023-07-19 10:04:54 +00:00
Ramon Gutierrez
6d6662611d Change dependency for fcm to forked lib with high priority set #751 2023-07-19 17:57:05 +08:00
Ramon Gutierrez
0eef2449e8 Fix duplicate fcm notification issue #746 2023-07-19 16:14:49 +08:00
Ramon Gutierrez
c2ff927f1f Merge branch '750-resq-2-fix-computation-issue-for-jo-from-resq-1-app' into '746-resq-2-0-final'
Fix the same issue in resq 2 app. #750

See merge request jankstudio/resq!865
2023-07-19 07:43:33 +00:00
Korina Cordero
4f70fc437c Fix the same issue in resq 2 app. #750 2023-07-19 07:41:48 +00:00
Ramon Gutierrez
bc47873ed1 Merge branch '750-resq-2-fix-computation-issue-for-jo-from-resq-1-app' into '746-resq-2-0-final'
Resolve "RESQ 2: Fix computation issue for JO from Resq 1 app"

See merge request jankstudio/resq!864
2023-07-19 07:18:09 +00:00
Korina Cordero
8427b3fb3d Fix adding of battery to invoice criteria for new JOs created from app. #750 2023-07-19 07:17:03 +00:00
Ramon Gutierrez
a78c08b7a2 Merge branch '749-resq-2-fix-api-responses-in-capi-calls' into '746-resq-2-0-final'
Resolve "RESQ 2: Fix API responses in CAPI calls"

See merge request jankstudio/resq!863
2023-07-18 06:20:55 +00:00
Korina Cordero
976acc26ad Change APIResponse namespace for CAPI controllers. #749 2023-07-18 06:16:41 +00:00
Ramon Gutierrez
11355f3eb2 Fix case issue on class name for api controller #746 2023-07-18 09:35:48 +08:00
Ramon Gutierrez
20301b6b98 Merge branch '748-fuel-price-update-for-resq-2' into '746-resq-2-0-final'
Resolve "Fuel Price Update for Resq 2"

See merge request jankstudio/resq!862
2023-07-13 13:43:27 +00:00
Korina Cordero
97917cf49b Updated fuel prices. #748 2023-07-12 07:49:30 +00:00
Ramon Gutierrez
f26d6284f3 Merge branch '744-new-invoice-service' into '746-resq-2-0-final'
Resolve "New invoice service"

See merge request jankstudio/resq!860
2023-07-10 12:48:30 +00:00
Korina Cordero
12ebe3538c Set taxable for the invoice criteria. #744 2023-07-07 09:36:22 +00:00
Korina Cordero
37ce74f07d Merge branch '746-resq-2-0-final' of gitlab.com:jankstudio/resq into 744-new-invoice-service 2023-07-06 05:39:07 -04:00
Korina Cordero
17e0fd6dcf Merge branch 'master' of gitlab.com:jankstudio/resq into 744-new-invoice-service 2023-07-06 05:25:22 -04:00
Korina Cordero
54c49eb8c5 Add comment on return value for compute. #744 2023-07-05 01:38:31 -04:00
Korina Cordero
2f3a77e96d Fix issues found during testing. #744 2023-07-05 01:36:44 -04:00
Korina Cordero
b4bcc06e42 Modify the invoice manager to be more generic. #744 2023-07-04 06:05:34 -04:00
Ramon Gutierrez
7beeef104c Update table names of role and user to match master #746 2023-06-23 13:14:41 +08:00
Ramon Gutierrez
87f77c9b9c Merge branch '730-resq-2-0' into 746-resq-2-0-final 2023-06-22 12:35:28 +08:00
Ramon Gutierrez
abc4e31d96 Initial commit for insurance listener only #730 2023-06-22 12:34:31 +08:00
Korina Cordero
783c26facc Add setting of taxable for invoice criteria. #744 2023-06-19 00:14:34 -04:00
Korina Cordero
822bc59c6c Set taxable for invoice criteria when app requests a job order. #744 2023-06-18 23:40:09 -04:00
Korina Cordero
9840ecf633 Use interface for the invoice manager. #744 2023-06-18 23:23:29 -04:00
ee9031a8f3 Merge branch '745-make-required-initial-concern-of-customer-and-notes-on-initial-concern-of-customer' into 'master'
Resolve "Make required Initial Concern of Customer and Notes on Initial Concern of Customer"

Closes #745

See merge request jankstudio/resq!859
2023-06-18 23:54:01 +00:00
Korina Cordero
f9800d8da3 Make required the initial concern and notes on initial concern fields. #745 2023-06-16 02:09:35 -04:00
Ramon Gutierrez
b81b10840b Pass jo status with each notification #730 2023-06-14 17:36:31 +08:00
Ramon Gutierrez
c2418b117e Attach jo_id to all fcm notifications #730 2023-06-14 17:14:59 +08:00
Ramon Gutierrez
31c0f4a102 Fix customer user not being properly associated with session #730 2023-06-14 16:49:04 +08:00
Ramon Gutierrez
c4878a9e6d Increase os_version size to 200 #730 2023-06-14 16:38:30 +08:00
Ramon Gutierrez
5540084416 Fix translation locations #730 2023-06-14 15:33:29 +08:00
Ramon Gutierrez
4990a3dc9d Add inital support for FCM push notifications 2023-06-13 20:33:58 +08:00
Korina Cordero
282760fe5a Remove debug message. #744 2023-06-07 05:33:59 -04:00
Korina Cordero
05073f3a0c Add frontend elements for testing JO saving. #744 2023-06-07 05:30:37 -04:00
Korina Cordero
fa514ef26c Remove debug messages. Add TODO task for front-end. #744 2023-06-05 21:51:19 -04:00
Korina Cordero
e9f18ed184 Modify the admin panel to use the new invoice engine when creating a new JO. #744 2023-06-05 21:34:15 -04:00
Korina Cordero
4ebbd8cfd3 Finish adding the test cases. #744 2023-06-05 20:13:37 -04:00
Korina Cordero
fe5121cf71 Add methods to process generateInvoice ajax call. Add test cases. #744 2023-06-05 01:55:04 -04:00
Korina Cordero
a986d7835b Add function to let the JO service call the new invoice manager. #744 2023-06-02 05:50:39 -04:00
Korina Cordero
77c2763f53 Fix typo. #744 2023-06-01 04:39:38 -04:00
Korina Cordero
f760b2c02a Add discount type rule and test cases. #744 2023-06-01 03:22:24 -04:00
Korina Cordero
725704e951 Add test cases for no tax. Fix issues found when computing the total if no tax. #744 2023-05-31 06:09:54 -04:00
Korina Cordero
49317188c9 Fix issues found during testing of tax computation. #744 2023-05-31 03:57:30 -04:00
Korina Cordero
f598e4f426 Add tax rule. #744 2023-05-30 05:54:16 -04:00
Korina Cordero
9bac6df2df Add rules for trade in and other service types. Add test scenarios. #744 2023-05-29 06:05:59 -04:00
Ramon Gutierrez
82065bda93 Fix review tag endpoints #730 2023-05-29 15:49:55 +08:00
Korina Cordero
e3ec6148df Add command to test invoice manager. #744 2023-05-26 05:54:22 -04:00
Korina Cordero
15830924e2 Add function to process the criteria entries. #744 2023-05-25 06:06:15 -04:00
Korina Cordero
0063f10f93 Add function to check what rules to use. #744 2023-05-24 06:16:44 -04:00
Korina Cordero
0bb32f47b5 Modify invoice engine. #744 2023-05-23 07:52:14 -04:00
Ramon Gutierrez
ed037428b3 Fix missing rider object from jo info #730 2023-05-23 14:41:43 +08:00
Ramon Gutierrez
0b3b5eb0bb Add review info to jo info endpoint #730 2023-05-23 14:32:46 +08:00
Korina Cordero
1c02138f52 Add services for the new invoice generator. #744 2023-05-22 06:06:12 -04:00
Ramon Gutierrez
562e96e32d Add review tag management views #730 2023-05-22 14:42:21 +08:00
Ramon Gutierrez
e235026197 Add review tags api #730 2023-05-22 14:12:33 +08:00
Ramon Gutierrez
a821c42f0d Add rider average rating to jo info #730 2023-05-22 13:18:01 +08:00
Ramon Gutierrez
33e6994aaa Add rider rating average computation #730 2023-05-22 13:00:54 +08:00
Ramon Gutierrez
c2c4be8b7b Fix missing entity dependency for rider reviews #730 2023-05-22 12:48:01 +08:00
Ramon Gutierrez
f2233d6680 Remove unnecessary entitymanager dependency on motolite events controller #730 2023-05-17 01:29:53 +08:00
Ramon Gutierrez
9afa2dfc6c Add CRM support for upcoming motolite events #730 2023-05-17 00:30:13 +08:00
Ramon Gutierrez
059bda9bf6 Add limit clause to motolite events endpoint #730 2023-05-16 13:30:12 +08:00
Ramon Gutierrez
9b7e0e55a7 Add missing getter method to motolite events #730 2023-05-16 12:20:05 +08:00
Ramon Gutierrez
24bd64d6e0 Fix events endpoint not returning any rows #730 2023-05-16 12:15:39 +08:00
Ramon Gutierrez
202b2d740e Fix case types on motolite event type namevalue #730 2023-05-16 12:12:01 +08:00
Ramon Gutierrez
8241dc5630 Fix order clause for upcoming events #730 2023-05-16 12:06:15 +08:00
Ramon Gutierrez
58eafabdcf Add api support for motolite events #730 2023-05-16 06:12:05 +08:00
Ramon Gutierrez
9c36baa3d3 Fix jo id null on outlet_assign #730 2023-05-12 15:32:02 +08:00
Ramon Gutierrez
551a39a236 Append JO ID and debug code to outlet_assign event #730 2023-05-12 15:21:15 +08:00
Ramon Gutierrez
8d7556c732 Remove in_transit mqtt event #730 2023-05-12 15:15:14 +08:00
Ramon Gutierrez
b90fc2f8dd Move in_transit event to pre hub departure #730 2023-05-12 15:10:08 +08:00
Ramon Gutierrez
c32b7d68c7 Add in_transit mqtt event for new app #730 2023-05-12 15:05:21 +08:00
Ramon Gutierrez
63526d65f2 Fix typo for Search in menu translation keys #730 2023-05-12 03:27:17 +08:00
Ramon Gutierrez
ea60fa081c Fix missing param for new mqtt client on assigning hub and rider #730 2023-05-11 19:13:56 +08:00
Ramon Gutierrez
4f06c02bd4 Restore legacy MQTTClient, add new MQTTClientApiv2 class for new customer api #730 2023-05-11 18:34:02 +08:00
Ramon Gutierrez
1d278beb35 Fix label for jumpstart service #730 2023-05-11 16:11:39 +08:00
Ramon Gutierrez
0afc1a04b0 Fix partner reviews not working with new customer session type #730 2023-05-11 16:11:17 +08:00
Ramon Gutierrez
fa3998d47f Separate apiv2 user provider #730 2023-05-11 01:39:38 +08:00
Ramon Gutierrez
a52267ca76 Initial implementation of insurance connector #730 2023-05-06 03:24:45 +08:00
Ramon Gutierrez
3861a9bf93 Keep legacy mqtt payload the same #730 2023-05-06 01:51:06 +08:00
Ramon Gutierrez
ddde274fdc Additional fix using sess array as object on mqtt connector #730 2023-05-06 01:37:07 +08:00
Ramon Gutierrez
82578fb346 Fix using sess array as object on mqtt connector #730 2023-05-06 01:36:21 +08:00
Ramon Gutierrez
e24174e59e Use different mqtt channel for apiv2 sessions #730 2023-05-06 01:33:16 +08:00
Ramon Gutierrez
5f4e6b1ad9 Fix mapping for customer user to session #730 2023-05-04 18:26:57 +08:00
Ramon Gutierrez
8e53a3bb19 Add debug code for mqtt sending #730 2023-05-04 18:08:50 +08:00
Ramon Gutierrez
219ab513b5 Remove truncation of first two digits on customer creation for new API #730 2023-05-04 18:07:38 +08:00
Ramon Gutierrez
87702f0e4c Revert use of isEmpty on arraycollection on mqtt connector #730 2023-05-04 17:41:19 +08:00
Ramon Gutierrez
df6a4ad292 Fix array merge issue on refactoring mqtt client #730 2023-05-04 17:38:10 +08:00
Ramon Gutierrez
13531171d7 Update MQTT client to send to both legacy and new customer sessions #730 2023-05-03 11:03:25 +08:00
Ramon Gutierrez
1ed25d0a26 Remove debug code from review entity #730 2023-04-28 18:14:54 +08:00
Ramon Gutierrez
1261fc1d20 Add support for new customer sessions on partner reviews #730 2023-04-28 18:08:23 +08:00
Ramon Gutierrez
3bf350ceaf Add icon id to service list #730 2023-04-28 17:47:58 +08:00
Ramon Gutierrez
4182e44607 Add date_schedule to JO info generation #730 2023-04-24 17:02:58 +08:00
Ramon Gutierrez
fed0b655f5 Make landmark field optional for now #730 2023-04-24 15:11:09 +08:00
Ramon Gutierrez
68fe13de46 Add missing dependency for warranty #730 2023-04-12 22:52:27 +08:00
Ramon Gutierrez
403de63251 Add missing dependency for warranty serial, replace request input api_key instances with session_key #730 2023-04-12 06:37:15 +08:00
Ramon Gutierrez
bddddf1970 Fix missing dependency on warranty check #730 2023-04-04 11:49:10 +08:00
da03f13f75 Merge branch '743-fix-for-hub-filter-for-emergency-jo-from-re-assign-hub' into 'master'
Resolve "Fix for hub filter for emergency JO from Re-assign Hub"

Closes #743

See merge request jankstudio/resq!857
2023-03-31 10:29:58 +00:00
Korina Cordero
2535498db9 Add TODO for emergency distance limit. #743 2023-03-31 10:29:22 +00:00
Korina Cordero
f6436cd421 Add limit distance for emergencies when assigning hub from Open page. #743 2023-03-31 10:04:16 +00:00
Ramon Gutierrez
46104bc395 Replace doctrine spatial lib with forked version #730 2023-03-28 11:36:32 +08:00
Ramon Gutierrez
cb11849489 Add support for customer credits, add credit manager #730 2023-03-27 04:40:14 +08:00
Ramon Gutierrez
6d416ee5f1 Fix issues encountered during v2 app creation #730 2023-03-26 00:43:45 +08:00
Ramon Gutierrez
c7fc06ef50 Fix routes and acl for apiv2 #730 2023-03-26 00:43:08 +08:00
bc7a91c01d Merge branch '740-filter-sms-hub-rejection-by-servie-type' into 'master'
Resolve "Filter SMS Hub Rejection by Service Type"

Closes #740

See merge request jankstudio/resq!855
2023-03-08 01:01:27 +00:00
Korina Cordero
92abe703d3 Add checking for JO service type. #740 2023-03-07 02:30:57 +00:00
e2498beca7 Merge branch '739-add-customer-location-to-jo' into 'master'
Resolve "Add customer location to JO"

Closes #739

See merge request jankstudio/resq!854
2023-02-28 10:14:33 +00:00
f6a858cc28 Merge branch '738-filter-sms-hub-rejection' into 'master'
Resolve "Filter SMS Hub Rejection"

Closes #738

See merge request jankstudio/resq!853
2023-02-28 10:14:20 +00:00
Korina Cordero
674a862586 Remove else condition. #738 2023-02-28 08:04:19 +00:00
Korina Cordero
e102766961 Move checking for phone number and rejection reason. #738 2023-02-28 08:02:11 +00:00
Korina Cordero
a594bef863 Add sql script to insert the requested customer locations. #739 2023-02-23 08:07:40 +00:00
Korina Cordero
2aef8e64e7 Add blank option for customer location dropdown. Add checking for blank option. #739 2023-02-23 08:02:37 +00:00
Korina Cordero
4717d7d925 Add customer location dropdown to JO form. #739 2023-02-23 07:13:48 +00:00
Korina Cordero
f57c9f5f4f Add CRUD for customer locations. #739 2023-02-23 03:08:17 +00:00
Ramon Gutierrez
d59ac805f7 Update rider app to use correct api response class #730 2023-02-18 23:28:04 +08:00
Ramon Gutierrez
95b5c59db2 Fix customer user not being associated with customer record #730 2023-02-18 15:43:20 +08:00
Ramon Gutierrez
202b984aa2 Fix typo on customer user getter #730 2023-02-18 15:43:02 +08:00
Ramon Gutierrez
f7f182e062 Fix old dependency for customer endpoints on rider app api #730 2023-02-16 05:56:24 +08:00
Ramon Gutierrez
0947b12e99 Add resend code endpoint to creds whitelist #730 2023-02-16 05:54:39 +08:00
Ramon Gutierrez
2f7e493fab Separate routes for old and new apis #730 2023-02-16 05:54:14 +08:00
Korina Cordero
5dc4d9f3f8 Add checking if reason is administrative. #738 2023-02-13 02:05:18 +00:00
Ramon Gutierrez
81d4f46930 Fix existing customer user association #730 2023-02-10 15:10:24 +08:00
Ramon Gutierrez
e3be92b63e Rename register return value from session_id to session_key #730 2023-02-08 07:45:01 +08:00
Ramon Gutierrez
f62ce78ac7 Fix base api user usage, fix customer user registration logic on number confirm #730 2023-02-08 07:42:51 +08:00
1ecd69b959 Fix typo in ApiUser entity #730 2023-02-08 02:45:20 +08:00
af39b6d6ca Add ApiUser to replace the old catalyst api user #730 2023-02-08 02:44:15 +08:00
Ramon Gutierrez
efec5c4b84 Add proper config for api acl #730 2023-02-08 02:24:25 +08:00
Ramon Gutierrez
693ee7cd9b Update api user provider on security policy #730 2023-02-07 18:19:55 +08:00
Ramon Gutierrez
e0029d8ab5 Remove debug code for register endpoint, add customer api user entity #730 2023-02-07 18:17:35 +08:00
Ramon Gutierrez
98705f99d4 Remove guard on api routes, move all returns to base accessed methods #730 2023-02-07 09:11:17 +08:00
Ramon Gutierrez
9876165af5 Adjust new customer api method groupings, add routes #730 2023-02-07 08:25:07 +08:00
Ramon Gutierrez
b3301fdbac Add new customer app API logic #730 2023-02-07 07:58:57 +08:00
Ramon Gutierrez
2a0ca3b9ea Update auth table names to new standard, fix model association config for acl #730 2023-02-06 05:14:08 +08:00
Ramon Gutierrez
876722ebe7 Switch over to newer catalyst libs, update service configs #730 2023-02-03 14:52:44 +08:00
237 changed files with 24950 additions and 2172 deletions

3
.gitignore vendored
View file

@ -12,3 +12,6 @@
*.swp
/public/warranty_uploads/*
.vscode
*__pycache__
/public/assets/images/insurance-premiums.png

View file

@ -1,9 +0,0 @@
<?php
namespace Catalyst\APIBundle\Access;
use Catalyst\AuthBundle\Service\ACLGenerator as BaseGenerator;
class Generator extends BaseGenerator
{
}

View file

@ -1,10 +0,0 @@
<?php
namespace Catalyst\APIBundle\Access;
use Catalyst\AuthBundle\Service\ACLVoter as BaseVoter;
class Voter extends BaseVoter
{
}

View file

@ -1,9 +0,0 @@
<?php
namespace Catalyst\APIBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class CatalystAPIBundle extends Bundle
{
}

View file

@ -1,155 +0,0 @@
<?php
namespace Catalyst\APIBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Catalyst\APIBundle\Connector\Client as APIClient;
class TestAPICommand extends Command
{
protected function configure()
{
$this->setName('api:test-connector-all')
->setDescription('Test API connector with all commands.')
->setHelp('Test API Connector with all commands.')
->addArgument('protocol', InputArgument::REQUIRED, 'protocol')
->addArgument('server', InputArgument::REQUIRED, 'server')
->addArgument('api_key', InputArgument::REQUIRED, 'api_key')
->addArgument('secret_key', InputArgument::REQUIRED, 'secret_key');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$protocol = $input->getArgument('protocol');
$server = $input->getArgument('server');
$api_key = $input->getArgument('api_key');
$secret_key = $input->getArgument('secret_key');
// api client
$api = new APIClient($server, $api_key, $secret_key);
$api->setProtocol($protocol);
// test
$api->get('/capi/test');
// TODO: shift this out of the bundle, since it's project specific
// warranty register
$serial = 'AJ34LJADR12134LKJL5';
$plate_num = 'XEN918';
$params = [
'serial' => $serial,
'plate_number' => $plate_num,
'warranty_class' => 'private',
'sku' => 'WMEB24CB-CPN00-LX',
'date_purchase' => '20181001',
'date_expire' => '20191001',
'first_name' => 'First',
'last_name' => 'Last',
'mobile_number' => '09231234567',
];
//$api->post('/capi/warranties', $params);
// get all warranties
$params = [
'order' => 'DESC',
'limit' => '5',
'start' => '1',
];
//$api->get('/capi/warranties', $params);
// warranty find
//$api->get('/capi/warranties/' . $serial);
// warranty update
$id = 86811;
$params = [
'serial' => $serial,
'plate_number' => $plate_num,
'warranty_class' => 'private',
'sku' => 'WMEB24CB-CPN00-LX',
'date_purchase' => '20181001',
'date_expire' => '20191001',
'first_name' => 'First',
'last_name' => 'Last',
'mobile_number' => '123456789111',
];
//$api->post('/capi/warranties/'. $id, $params);
// warranty set privacy policy
$id = 86811;
$policy_id = 2;
$params = [
'privacy_policy_id' => $policy_id,
];
//$api->post('/capi/warranties/' . $id .'/privacypolicy', $params);
// warranty claim
$id = 86811;
$serial = 'AJ34LJADR12134LKJL5';
$params = [
'serial' => $serial,
];
//$api->post('/capi/warranties/' . $id . '/claim', $params);
// warranty cancel
$id = 86811;
//$api->get('/capi/warranties/' . $id . '/cancel');
// plate warranty
//$api->get('/capi/plates/' . $plate_num . '/warranties');
// warranty delete
$id = 86811;
//$api->post('/capi/warranties/' . $id . '/delete');
// battery
//$api->get('/capi/battery_brands');
//$api->get('/capi/battery_sizes');
//$api->get('/capi/batteries');
// vehicle
//$api->get('/capi/vehicle_manufacturers');
//$api->get('/capi/vehicles');
// privacy policy
$privacy_policy_id = 2;
//$api->get('/capi/privacy_policy/' . $privacy_policy_id );
// register new customer
$params = [
'first_name' => 'Krispups',
'last_name' =>'Porzindog',
'mobile_number' => '9221111111',
'v_make_id' => '22241',
'v_model_year' => '2018',
'v_plate_number' => 'KPP1234',
'v_color' => 'White',
'v_condition' => 'new',
'v_fuel_type' => 'gas',
];
//$api->post('/capi/quick_registration', $params);
// get warranties given list of serial numbers
$serial_list = [
'AJ34LJADR12134LKJM4',
'AJ34LJADR12134LKJL5',
'test',
];
$params = [
'serial_list' => $serial_list,
];
$api->post('/capi/warranties_list', $params);
}
}

View file

@ -1,104 +0,0 @@
<?php
namespace Catalyst\APIBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Catalyst\APIBundle\Connector\Client as APIClient;
class TestCommand extends Command
{
protected function configure()
{
$this->setName('api:test-connector')
->setDescription('Test API connector.')
->setHelp('Test API Connector.')
->addArgument('protocol', InputArgument::REQUIRED, 'protocol')
->addArgument('server', InputArgument::REQUIRED, 'server')
->addArgument('api_key', InputArgument::REQUIRED, 'api_key')
->addArgument('secret_key', InputArgument::REQUIRED, 'secret_key');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$protocol = $input->getArgument('protocol');
$server = $input->getArgument('server');
$api_key = $input->getArgument('api_key');
$secret_key = $input->getArgument('secret_key');
// api client
$api = new APIClient($server, $api_key, $secret_key);
$api->setProtocol($protocol);
// test
$api->get('/capi/test');
// TODO: shift this out of the bundle, since it's project specific
// warranty register
$serial = 'AJ34LJADR12134LKJL5';
$plate_num = 'XEN918';
$params = [
'serial' => $serial,
'plate_number' => $plate_num,
'warranty_class' => 'private',
'sku' => 'WMEB24CB-CPN00-LX',
'date_purchase' => '20181001',
'date_expire' => '20191001',
'first_name' => 'First',
'last_name' => 'Last',
'mobile_number' => '12345678910',
];
$api->post('/capi/warranties', $params);
// get all warranties
$api->get('/capi/warranties');
/*
// warranty find
$api->get('/capi/warranties/' . $serial);
*/
// warranty claim
$id = 86811;
$serial = 'AJ34LJADR12134LKJL';
$params = [
'serial' => $serial,
];
$api->post('/capi/warranties/' . $id . '/claim', $params);
// add battery
$sku = 'WZMB31QT-CPP00-S';
$brand_id = '4';
$size_id = '1';
$params = [
'sku' => $sku,
'brand_id' => $brand_id,
'size_id' => $size_id,
];
$api->post('/capi/batteries', $params);
/*
// plate warranty
$api->get('/capi/plates/' . $plate_num . '/warranties');
// battery
$api->get('/capi/battery_brands');
$api->get('/capi/battery_sizes');
$api->get('/capi/batteries');
// vehicle
// $api->get('/capi/vehicle_manufacturers');
// $api->get('/capi/vehicles');
*/
}
}

View file

@ -1,49 +0,0 @@
<?php
namespace Catalyst\APIBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Entity\User;
class UserCreateCommand extends Command
{
protected $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
parent::__construct();
}
protected function configure()
{
$this->setName('api:user-create')
->setDescription('Create new API user.')
->setHelp('Creates new API user and saves to database.')
->addArgument('name', InputArgument::REQUIRED, 'name');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
$user = new User();
$user->setName($name);
$this->em->persist($user);
$this->em->flush();
$output->write('API Key - ' . $user->getAPIKey() . "\n");
$output->write('Secret Key - ' . $user->getSecretKey() . "\n");
}
}

View file

@ -1,151 +0,0 @@
<?php
namespace Catalyst\APIBundle\Connector;
use DateTime;
class Client
{
const HEADER_API_KEY = 'X-Cata-API-Key';
const HEADER_SIGNATURE = 'X-Cata-Signature';
const HEADER_DATE = 'X-Cata-Date';
const DATE_FORMAT = 'D, d M Y H:i:s T';
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36';
protected $protocol;
protected $server;
protected $port;
protected $api_key;
protected $secret_key;
protected $curl;
public function __construct($server, $api_key, $secret_key)
{
$this->protocol = 'https';
$this->port = null;
$this->server = $server;
$this->api_key = $api_key;
$this->secret_key = $secret_key;
$this->curl = curl_init();
}
public function __destruct()
{
curl_close($this->curl);
}
public function setProtocol($protocol)
{
if ($protocol != 'http' && $protocol != 'https')
return $this;
$this->protocol = $protocol;
return $this;
}
protected function getDateString()
{
$date = new DateTime();
return $date->format(self::DATE_FORMAT);
}
public function get($url, $params = [])
{
curl_reset($this->curl);
$date_string = $this->getDateString();
$headers = $this->generateHeaders('GET', $url, $date_string);
// build query string
if (count($params) > 0)
$query_string = '?' . http_build_query($params);
else
$query_string = '';
// build url
if ($this->port == null)
$full_url = $this->protocol . '://' . $this->server . $url . $query_string;
else
$full_url = $this->protocol . '://' . $this->server . ':' . $this->port . $url . $query_string;
error_log($full_url);
// curl
// curl_setopt($this->curl, CURLOPT_VERBOSE, true);
curl_setopt($this->curl, CURLOPT_URL, $full_url);
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($this->curl, CURLOPT_USERAGENT, self::USER_AGENT);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->curl, CURLOPT_TIMEOUT, 0);
$res = curl_exec($this->curl);
error_log($res);
}
public function post($url, $params = [])
{
curl_reset($this->curl);
$date_string = $this->getDateString();
$headers = $this->generateHeaders('POST', $url, $date_string);
// build query string
$query_string = http_build_query($params);
// build url
if ($this->port == null)
$full_url = $this->protocol . '://' . $this->server . $url;
else
$full_url = $this->protocol . '://' . $this->server . ':' . $this->port . $url;
error_log($full_url);
// curl
// curl_setopt($this->curl, CURLOPT_VERBOSE, true);
curl_setopt($this->curl, CURLOPT_URL, $full_url);
curl_setopt($this->curl, CURLOPT_POST, true);
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $query_string);
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($this->curl, CURLOPT_USERAGENT, self::USER_AGENT);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->curl, CURLOPT_TIMEOUT, 0);
$res = curl_exec($this->curl);
error_log($res);
}
protected function generateSignature($method, $url, $date_string)
{
$creds = [
$method,
$url,
$date_string,
$this->secret_key,
];
$sig_source = implode('|', $creds);
error_log('SIG SOURCE - ' . $sig_source);
$raw_sig = hash_hmac('sha1', $sig_source, $this->secret_key, true);
$enc_sig = base64_encode($raw_sig);
return $enc_sig;
}
protected function generateHeaders($method, $url, $date_string)
{
$sig = $this->generateSignature($method, $url, $date_string);
$headers = [
self::HEADER_API_KEY . ': ' . $this->api_key,
self::HEADER_SIGNATURE . ': ' . $sig,
self::HEADER_DATE . ': ' . $date_string,
];
return $headers;
}
}

View file

@ -1,42 +0,0 @@
<?php
namespace Catalyst\APIBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
abstract class APIController extends Controller
{
protected function checkRequiredParameters(Request $req, $params = [])
{
$missing = [];
// check if parameters are there
foreach ($params as $param)
{
if ($req->getMethod() == 'GET')
{
$check = $req->query->get($param);
if (empty($check))
$missing[] = $param;
}
// else if ($req->getMethod() == 'POST')
else
{
$check = $req->request->get($param);
//if (empty($check))
if (!isset($check))
$missing[] = $param;
}
}
// check missing parameters
if (count($missing) > 0)
{
$miss_string = implode(', ', $missing);
return 'Missing required parameter(s): ' . $miss_string;
}
return false;
}
}

View file

@ -1,20 +0,0 @@
<?php
namespace Catalyst\APIBundle\DataFixtures;
use Catalyst\APIBundle\Entity\Role;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
class APIRoleFixtures extends Fixture
{
public function load(ObjectManager $em)
{
// setup super user account
$role = new Role();
$role->setID(Role::SUPER_ADMIN)
->setName('Super Administrator');
$em->persist($role);
$em->flush();
}
}

View file

@ -1,28 +0,0 @@
<?php
namespace Catalyst\APIBundle\Entity;
use Catalyst\AuthBundle\Entity\Role as BaseRole;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity
* @ORM\Table(name="api_role")
* @UniqueEntity("id")
* @UniqueEntity("name")
*/
class Role extends BaseRole
{
/**
* @ORM\ManyToMany(targetEntity="User", mappedBy="roles", fetch="EXTRA_LAZY")
*/
protected $users;
public function __construct()
{
parent::__construct();
}
}

View file

@ -1,186 +0,0 @@
<?php
namespace Catalyst\APIBundle\Entity;
use Catalyst\AuthBundle\Entity\User as BaseUser;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\Common\Collections\ArrayCollection;
use DateTime;
/**
* @ORM\Entity
* @ORM\Table(name="api_user")
*/
class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
// api key
/**
* @ORM\Column(type="string", length=32)
*/
protected $api_key;
// secret key
/**
* @ORM\Column(type="string", length=32)
*/
protected $secret_key;
/**
* @ORM\Column(type="string", length=80)
*/
protected $name;
// date created
/**
* @ORM\Column(type="datetime")
*/
protected $date_create;
// roles
/**
* @ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* @ORM\JoinTable(name="api_user_role")
*/
protected $roles;
// rider linked to user
// NOTE: we're directly linking this only because we don't have to care about other apps using this library
/**
* @ORM\OneToOne(targetEntity="App\Entity\Rider", mappedBy="api_user")
*/
protected $rider;
/**
* @ORM\Column(type="json")
*/
protected $metadata;
public function __construct()
{
parent::__construct();
// generate keys
$this->setAPIKey($this->generateAPIKey())
->setSecretKey($this->generateSecretKey());
// set date created
$this->date_create = new DateTime();
$this->metadata = [];
}
public function getID()
{
return $this->id;
}
public function setAPIKey($api_key)
{
$this->api_key = $api_key;
return $this;
}
public function getAPIKey()
{
return $this->api_key;
}
public function setSecretKey($key)
{
$this->secret_key = $key;
return $this;
}
public function getSecretKey()
{
return $this->secret_key;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function getDateCreate()
{
return $this->date_create;
}
public function getPassword()
{
// we don't need this for API
return 'notneeded';
}
public function getSalt()
{
return null;
}
public function getUsername()
{
// since it's an api, the api key IS the username
return $this->api_key;
}
public function eraseCredentials()
{
return;
}
public function generateAPIKey()
{
return $this->generateKey('api');
}
public function generateSecretKey()
{
return $this->generateKey('secret');
}
public function setMetadata($meta)
{
$this->metadata = $meta;
return $this;
}
public function getMetadata()
{
if ($this->metadata == null)
return [];
return $this->metadata;
}
protected function generateKey($prefix = '')
{
return md5(uniqid($prefix, true));
}
public function setRider($rider)
{
$this->rider = $rider;
return $this;
}
public function getRider()
{
return $this->rider;
}
}

View file

@ -1,20 +0,0 @@
<?php
namespace Catalyst\APIBundle\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
class APIResponse extends JsonResponse
{
public function __construct($success = true, $message = '', $data = null, $status = 200, $headers = [])
{
$data = [
'success' => (bool) $success,
'message' => (string) $message,
'data' => $data,
];
parent::__construct($data, $status, $headers);
}
}

View file

@ -1,160 +0,0 @@
<?php
namespace Catalyst\APIBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Doctrine\ORM\EntityManagerInterface;
use DateTime;
class APIKeyAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface
{
const HEADER_API_KEY = 'X-Cata-API-Key';
const HEADER_SIGNATURE = 'X-Cata-Signature';
const HEADER_DATE = 'X-Cata-Date';
const DATE_FORMAT = 'D, d M Y H:i:s T';
// 30 minute time limit
const TIME_LIMIT = 1800;
protected $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
protected function validateSignature($creds, $secret_key)
{
$elements = [
$creds['method'],
$creds['uri'],
$creds['date'],
$secret_key,
];
$sig_source = implode('|', $elements);
error_log($sig_source);
// generate signature
$raw_sig = hash_hmac('sha1', $sig_source, $secret_key, true);
$enc_sig = base64_encode($raw_sig);
error_log($enc_sig);
if ($enc_sig != trim($creds['signature']))
throw new CustomUserMessageAuthenticationException('Invalid signature.');
}
public function createToken(Request $req, $provider_key)
{
// api key header
$api_key = $req->headers->get(self::HEADER_API_KEY);
if ($api_key == null)
throw new BadCredentialsException('No API key sent.');
// check date from headers
$hdate_string = $req->headers->get(self::HEADER_DATE);
if ($hdate_string == null)
throw new BadCredentialsException('No date specified.');
$hdate = DateTime::createFromFormat(self::DATE_FORMAT, $hdate_string);
if ($hdate == null)
throw new BadCredentialsException('Invalid date specified.');
// get number of seconds difference
$date_now = new DateTime();
$date_diff = abs($date_now->getTimestamp() - $hdate->getTimestamp());
// time difference is too much
if ($date_diff > self::TIME_LIMIT)
throw new BadCredentialsException('Clock synchronization error.');
// signature header
$sig = $req->headers->get(self::HEADER_SIGNATURE);
if ($sig == null)
throw new BadCredentialsException('No signature sent.');
// credentials
$creds = [
'api_key' => $api_key,
'date' => $hdate_string,
'signature' => $sig,
'method' => $req->getRealMethod(),
'uri' => $req->getPathInfo(),
];
return new PreAuthenticatedToken(
'anonymous',
$creds,
$provider_key
);
}
public function supportsToken(TokenInterface $token, $provider_key)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $provider_key;
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $user_provider, $provider_key)
{
if (!$user_provider instanceof APIKeyUserProvider)
{
throw new \InvalidArgumentException(
sprintf(
'The user provider must be an instance of APIKeyUserProvider (%s was given).',
get_class($user_provider)
)
);
}
$creds = $token->getCredentials();
$api_key = $creds['api_key'];
$user = $user_provider->getUserByAPIKey($api_key);
// check if api key is valid
if (!$user)
throw new CustomUserMessageAuthenticationException('Invalid API Key');
// check if signature is valid
$this->validateSignature($creds, $user->getSecretKey());
// check if user is enabled
if (!$user->isEnabled())
{
throw new CustomUserMessageAuthenticationException('User account is disabled');
}
// $user = $user_provider->loadUserByUsername($username);
return new PreAuthenticatedToken(
$user,
$api_key,
$provider_key,
$user->getRoles()
);
}
public function onAuthenticationFailure(Request $req, AuthenticationException $exception)
{
$data = [
'success' => false,
'error' => [
'message' => $exception->getMessage(),
],
];
return new JsonResponse($data, 401);
}
}

View file

@ -1,62 +0,0 @@
<?php
namespace Catalyst\APIBundle\Security;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Entity\User;
class APIKeyUserProvider implements UserProviderInterface
{
protected $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function getUserByAPIKey($api_key)
{
$user = $this->em->getRepository(User::class)->findOneBy(array('api_key' => $api_key));
return $user;
}
public function getUsernameForAPIKey($apiKey)
{
// Look up the username based on the token in the database, via
// an API call, or do something entirely different
$username = 'test';
return $username;
}
public function loadUserByUsername($username)
{
return new User(
$username,
null,
// the roles for the user - you may choose to determine
// these dynamically somehow based on the user
array('ROLE_API')
);
}
public function refreshUser(UserInterface $user)
{
// this is used for storing authentication in the session
// but in this example, the token is sent in each request,
// so authentication can be stateless. Throwing this exception
// is proper to make things stateless
throw new UnsupportedUserException();
}
public function supportsClass($class)
{
return User::class === $class;
}
}

View file

@ -1,18 +0,0 @@
<?php
namespace Catalyst\APIBundle\Service;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
class AccessDeniedHandler implements AccessDeniedHandlerInterface
{
public function handle(Request $req, AccessDeniedException $exception)
{
$content = $exception->getMessage();
return new Response($content, 403);
}
}

View file

@ -4,28 +4,41 @@
"repositories": [
{
"type": "vcs",
"url": "git@gitlab.com:jankstudio-catalyst/auth-bundle.git"
"url": "https://github.com/jankstudio/doctrine2-spatial.git"
},
{
"type": "vcs",
"url": "git@gitlab.com:jankstudio-catalyst/menu-bundle.git"
"url": "git@gitlab.com:jankstudio1/catalyst-2/api-bundle.git"
},
{
"type": "vcs",
"url": "git@gitlab.com:jankstudio1/catalyst-2/auth-bundle.git"
},
{
"type": "vcs",
"url": "git@gitlab.com:jankstudio1/catalyst-2/menu-bundle.git"
},
{
"type": "vcs",
"url": "https://github.com/arcticzero/php-fcm.git"
}
],
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"catalyst/auth-bundle": "dev-master",
"catalyst/menu-bundle": "dev-master",
"composer/package-versions-deprecated": "1.11.99.4",
"creof/doctrine2-spatial": "^1.2",
"data-dog/audit-bundle": "^0.1.10",
"doctrine/common": "^2",
"doctrine/doctrine-bundle": "^2",
"doctrine/doctrine-migrations-bundle": "^2",
"doctrine/orm": "^2",
"edwinhoksberg/php-fcm": "^1.0",
"edwinhoksberg/php-fcm": "dev-notif-priority-hotfix",
"guzzlehttp/guzzle": "^6.3",
"hashids/hashids": "^4.1",
"jankstudio/catalyst-api-bundle": "dev-master",
"jankstudio/catalyst-auth-bundle": "dev-master",
"jankstudio/catalyst-menu-bundle": "dev-master",
"jankstudio/doctrine-spatial": "dev-master",
"microsoft/azure-storage-blob": "^1.5",
"predis/predis": "^1.1",
"sensio/framework-extra-bundle": "^5.1",
@ -65,8 +78,7 @@
},
"autoload": {
"psr-4": {
"App\\": "src/",
"Catalyst\\APIBundle\\": "catalyst/api-bundle/"
"App\\": "src/"
}
},
"autoload-dev": {

454
composer.lock generated
View file

@ -4,90 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5b5acb546514b41ab347ef37b85425c9",
"content-hash": "653f8558c75614dd65421cb3eb48c29b",
"packages": [
{
"name": "catalyst/auth-bundle",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://gitlab.com/jankstudio-catalyst/auth-bundle.git",
"reference": "803e38f58907513c8df30c31d51303b68c645a17"
},
"dist": {
"type": "zip",
"url": "https://gitlab.com/api/v4/projects/jankstudio-catalyst%2Fauth-bundle/repository/archive.zip?sha=803e38f58907513c8df30c31d51303b68c645a17",
"reference": "803e38f58907513c8df30c31d51303b68c645a17",
"shasum": ""
},
"require": {
"doctrine/dbal": "^2.5.12",
"doctrine/doctrine-cache-bundle": "~1.2",
"php": "^7.0",
"symfony/framework-bundle": "~4.0"
},
"default-branch": true,
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Catalyst\\AuthBundle\\": ""
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Kendrick Chan",
"email": "kc@jankstudio.com"
}
],
"support": {
"issues": "https://gitlab.com/api/v4/projects/12709261/issues"
},
"time": "2019-07-01T09:45:41+00:00"
},
{
"name": "catalyst/menu-bundle",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://gitlab.com/jankstudio-catalyst/menu-bundle.git",
"reference": "2cf9a05862fcc6f956da4b095962915e07f14f64"
},
"dist": {
"type": "zip",
"url": "https://gitlab.com/api/v4/projects/jankstudio-catalyst%2Fmenu-bundle/repository/archive.zip?sha=2cf9a05862fcc6f956da4b095962915e07f14f64",
"reference": "2cf9a05862fcc6f956da4b095962915e07f14f64",
"shasum": ""
},
"require": {
"doctrine/dbal": "^2.5.12",
"doctrine/doctrine-cache-bundle": "~1.2",
"php": "^7.0",
"symfony/framework-bundle": "~4.0"
},
"default-branch": true,
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Catalyst\\MenuBundle\\": ""
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Kendrick Chan",
"email": "kc@jankstudio.com"
}
],
"support": {
"issues": "https://gitlab.com/api/v4/projects/12712039/issues"
},
"time": "2019-06-14T09:43:30+00:00"
},
{
"name": "composer/package-versions-deprecated",
"version": "1.11.99.4",
@ -161,67 +79,6 @@
],
"time": "2021-09-13T08:41:34+00:00"
},
{
"name": "creof/doctrine2-spatial",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/creof/doctrine2-spatial.git",
"reference": "58ea5fae1c1b450ee08d7dac25cd9e8f5e6fdebd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/creof/doctrine2-spatial/zipball/58ea5fae1c1b450ee08d7dac25cd9e8f5e6fdebd",
"reference": "58ea5fae1c1b450ee08d7dac25cd9e8f5e6fdebd",
"shasum": ""
},
"require": {
"creof/geo-parser": "~2.0",
"creof/wkb-parser": "~2.0",
"creof/wkt-parser": "~2.0",
"doctrine/orm": ">=2.3"
},
"require-dev": {
"phpunit/phpcov": "*",
"phpunit/phpunit": "<5.0",
"satooshi/php-coveralls": "~1.0"
},
"type": "library",
"autoload": {
"psr-0": {
"CrEOF\\Spatial": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Derek Lambert",
"email": "dlambert@dereklambert.com"
}
],
"description": "Doctrine2 multi-platform support for spatial types and functions",
"keywords": [
"database",
"dbal",
"geography",
"geometry",
"gis",
"mysql",
"opengis",
"orm",
"postgis",
"postgresql",
"spatial"
],
"support": {
"issues": "https://github.com/creof/doctrine2-spatial/issues",
"source": "https://github.com/creof/doctrine2-spatial/tree/master"
},
"time": "2017-07-13T16:48:25+00:00"
},
{
"name": "creof/geo-parser",
"version": "2.2.1",
@ -1069,102 +926,6 @@
],
"time": "2021-05-06T19:21:22+00:00"
},
{
"name": "doctrine/doctrine-cache-bundle",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/DoctrineCacheBundle.git",
"reference": "6bee2f9b339847e8a984427353670bad4e7bdccb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/DoctrineCacheBundle/zipball/6bee2f9b339847e8a984427353670bad4e7bdccb",
"reference": "6bee2f9b339847e8a984427353670bad4e7bdccb",
"shasum": ""
},
"require": {
"doctrine/cache": "^1.4.2",
"doctrine/inflector": "^1.0",
"php": "^7.1",
"symfony/doctrine-bridge": "^3.4|^4.0"
},
"require-dev": {
"instaclick/coding-standard": "~1.1",
"instaclick/object-calisthenics-sniffs": "dev-master",
"instaclick/symfony2-coding-standard": "dev-remaster",
"phpunit/phpunit": "^7.0",
"predis/predis": "~0.8",
"satooshi/php-coveralls": "^1.0",
"squizlabs/php_codesniffer": "~1.5",
"symfony/console": "^3.4|^4.0",
"symfony/finder": "^3.4|^4.0",
"symfony/framework-bundle": "^3.4|^4.0",
"symfony/phpunit-bridge": "^3.4|^4.0",
"symfony/security-acl": "^2.8",
"symfony/validator": "^3.4|^4.0",
"symfony/yaml": "^3.4|^4.0"
},
"suggest": {
"symfony/security-acl": "For using this bundle to cache ACLs"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Bundle\\DoctrineCacheBundle\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Fabio B. Silva",
"email": "fabio.bat.silva@gmail.com"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@hotmail.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Doctrine Project",
"homepage": "http://www.doctrine-project.org/"
}
],
"description": "Symfony Bundle for Doctrine Cache",
"homepage": "https://www.doctrine-project.org",
"keywords": [
"cache",
"caching"
],
"support": {
"issues": "https://github.com/doctrine/DoctrineCacheBundle/issues",
"source": "https://github.com/doctrine/DoctrineCacheBundle/tree/1.4.0"
},
"abandoned": true,
"time": "2019-11-29T11:22:01+00:00"
},
{
"name": "doctrine/doctrine-migrations-bundle",
"version": "2.2.3",
@ -2011,26 +1772,26 @@
},
{
"name": "edwinhoksberg/php-fcm",
"version": "v1.2.0",
"version": "dev-notif-priority-hotfix",
"source": {
"type": "git",
"url": "https://github.com/EdwinHoksberg/php-fcm.git",
"reference": "660bbe4ae71e02090f16b11b4a993dfebab83f7f"
"url": "https://github.com/ArcticZero/php-fcm.git",
"reference": "eabbfbc5df8df908ea7d13f0446202fda81e65fd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/EdwinHoksberg/php-fcm/zipball/660bbe4ae71e02090f16b11b4a993dfebab83f7f",
"reference": "660bbe4ae71e02090f16b11b4a993dfebab83f7f",
"url": "https://api.github.com/repos/ArcticZero/php-fcm/zipball/eabbfbc5df8df908ea7d13f0446202fda81e65fd",
"reference": "eabbfbc5df8df908ea7d13f0446202fda81e65fd",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.3 || ^7.0",
"php": ">= 7.2"
"php": ">= 7.3"
},
"require-dev": {
"mockery/mockery": "^1.0",
"php-coveralls/php-coveralls": "^2.3",
"phpunit/phpunit": "^6.5"
"mockery/mockery": "^1.5",
"php-coveralls/php-coveralls": "^2.5",
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
@ -2038,7 +1799,11 @@
"Fcm\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"scripts": {
"test": [
"XDEBUG_MODE=coverage vendor/bin/phpunit -c phpunit.dist.xml"
]
},
"license": [
"MIT"
],
@ -2051,17 +1816,16 @@
"description": "A library for sending Firebase cloud messages and managing user topic subscriptions, device groups and devices.",
"homepage": "https://github.com/EdwinHoksberg/php-fcm",
"keywords": [
"FCM",
"Firebase Cloud Messaging",
"fcm",
"firebase",
"firebase cloud messaging",
"google",
"notifications"
],
"support": {
"issues": "https://github.com/EdwinHoksberg/php-fcm/issues",
"source": "https://github.com/EdwinHoksberg/php-fcm/tree/v1.2.0"
"source": "https://github.com/ArcticZero/php-fcm/tree/master"
},
"time": "2021-01-19T01:15:30+00:00"
"time": "2023-07-19T09:04:27+00:00"
},
{
"name": "friendsofphp/proxy-manager-lts",
@ -2480,6 +2244,177 @@
},
"time": "2020-11-26T19:24:33+00:00"
},
{
"name": "jankstudio/catalyst-api-bundle",
"version": "dev-master",
"source": {
"type": "git",
"url": "git@gitlab.com:jankstudio1/catalyst-2/api-bundle.git",
"reference": "042d16534265adf81ae67c803b1ae8f0a94f3bb6"
},
"dist": {
"type": "zip",
"url": "https://gitlab.com/api/v4/projects/jankstudio1%2Fcatalyst-2%2Fapi-bundle/repository/archive.zip?sha=042d16534265adf81ae67c803b1ae8f0a94f3bb6",
"reference": "042d16534265adf81ae67c803b1ae8f0a94f3bb6",
"shasum": ""
},
"default-branch": true,
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Catalyst\\ApiBundle\\": ""
}
},
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Kendrick Chan",
"email": "kc@jankstudio.com"
}
],
"support": {
"source": "https://gitlab.com/jankstudio1/catalyst-2/api-bundle/-/tree/master",
"issues": "https://gitlab.com/jankstudio1/catalyst-2/api-bundle/-/issues"
},
"time": "2022-10-09T14:02:41+08:00"
},
{
"name": "jankstudio/catalyst-auth-bundle",
"version": "dev-master",
"source": {
"type": "git",
"url": "git@gitlab.com:jankstudio1/catalyst-2/auth-bundle.git",
"reference": "50f18ac6d4f6198b7bfe888487e0a4009193403e"
},
"dist": {
"type": "zip",
"url": "https://gitlab.com/api/v4/projects/jankstudio1%2Fcatalyst-2%2Fauth-bundle/repository/archive.zip?sha=50f18ac6d4f6198b7bfe888487e0a4009193403e",
"reference": "50f18ac6d4f6198b7bfe888487e0a4009193403e",
"shasum": ""
},
"require": {
"symfony/cache": "^5.0"
},
"default-branch": true,
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Catalyst\\AuthBundle\\": ""
}
},
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Kendrick Chan",
"email": "kc@jankstudio.com"
}
],
"support": {
"source": "https://gitlab.com/jankstudio1/catalyst-2/auth-bundle/-/tree/master",
"issues": "https://gitlab.com/jankstudio1/catalyst-2/auth-bundle/-/issues"
},
"time": "2022-01-19T16:21:57+08:00"
},
{
"name": "jankstudio/catalyst-menu-bundle",
"version": "dev-master",
"source": {
"type": "git",
"url": "git@gitlab.com:jankstudio1/catalyst-2/menu-bundle.git",
"reference": "9cddeecbbbb7d61868e1b319d9abc8eda4f34ab0"
},
"dist": {
"type": "zip",
"url": "https://gitlab.com/api/v4/projects/jankstudio1%2Fcatalyst-2%2Fmenu-bundle/repository/archive.zip?sha=9cddeecbbbb7d61868e1b319d9abc8eda4f34ab0",
"reference": "9cddeecbbbb7d61868e1b319d9abc8eda4f34ab0",
"shasum": ""
},
"default-branch": true,
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Catalyst\\MenuBundle\\": ""
}
},
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Kendrick Chan",
"email": "kc@jankstudio.com"
}
],
"support": {
"source": "https://gitlab.com/jankstudio1/catalyst-2/menu-bundle/-/tree/master",
"issues": "https://gitlab.com/jankstudio1/catalyst-2/menu-bundle/-/issues"
},
"time": "2022-10-07T11:55:32+08:00"
},
{
"name": "jankstudio/doctrine-spatial",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/jankstudio/doctrine-spatial.git",
"reference": "32b0fd126f3d81758ee74363535a44ccb3adae19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jankstudio/doctrine-spatial/zipball/32b0fd126f3d81758ee74363535a44ccb3adae19",
"reference": "32b0fd126f3d81758ee74363535a44ccb3adae19",
"shasum": ""
},
"require": {
"creof/geo-parser": "~2.0",
"creof/wkb-parser": "~2.0",
"creof/wkt-parser": "~2.0",
"doctrine/orm": ">=2.3"
},
"require-dev": {
"phpunit/phpcov": "*",
"phpunit/phpunit": "<5.0",
"satooshi/php-coveralls": "~1.0"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-0": {
"CrEOF\\Spatial": "lib/"
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Derek Lambert",
"email": "dlambert@dereklambert.com"
}
],
"description": "Doctrine2 multi-platform support for spatial types and functions",
"keywords": [
"database",
"dbal",
"geography",
"geometry",
"gis",
"mysql",
"opengis",
"orm",
"postgis",
"postgresql",
"spatial"
],
"support": {
"source": "https://github.com/jankstudio/doctrine-spatial/tree/master"
},
"time": "2021-08-23T01:41:57+00:00"
},
{
"name": "laminas/laminas-code",
"version": "3.4.1",
@ -8021,8 +7956,11 @@
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"catalyst/auth-bundle": 20,
"catalyst/menu-bundle": 20
"edwinhoksberg/php-fcm": 20,
"jankstudio/catalyst-api-bundle": 20,
"jankstudio/catalyst-auth-bundle": 20,
"jankstudio/catalyst-menu-bundle": 20,
"jankstudio/doctrine-spatial": 20
},
"prefer-stable": false,
"prefer-lowest": false,
@ -8031,5 +7969,5 @@
"ext-iconv": "*"
},
"platform-dev": [],
"plugin-api-version": "2.0.0"
"plugin-api-version": "2.3.0"
}

View file

@ -586,3 +586,17 @@ access_keys:
label: Update
- id: ownership_type.delete
label: Delete
- id: customer_location
label: Customer Location Access
acls:
- id: cust_location.menu
label: Menu
- id: cust_location.list
label: List
- id: cust_location.add
label: Add
- id: cust_location.update
label: Update
- id: cust_location.delete
label: Delete

View file

@ -2,7 +2,6 @@
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
@ -11,8 +10,8 @@ return [
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Catalyst\APIBundle\CatalystAPIBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Catalyst\ApiBundle\CatalystApiBundle::class => ['all' => true],
Catalyst\AuthBundle\CatalystAuthBundle::class => ['all' => true],
Catalyst\MenuBundle\CatalystMenuBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
];

View file

@ -42,20 +42,6 @@ services:
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
Catalyst\AuthBundle\Service\ACLGenerator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
$acl_file: "%app_acl_file%"
Catalyst\AuthBundle\Service\ACLVoter:
arguments:
$user_class: "App\\Entity\\User"
tags: ['security.voter']
Catalyst\AuthBundle\Service\UserChecker:
App\Service\FileUploader:
arguments:
$target_dir: '%image_upload_directory%'
@ -114,50 +100,6 @@ services:
arguments:
$redis_client: "@App\\Service\\RedisClientProvider"
Catalyst\APIBundle\Security\APIKeyUserProvider:
arguments:
$em: "@doctrine.orm.entity_manager"
Catalyst\APIBundle\Security\APIKeyAuthenticator:
arguments:
$em: "@doctrine.orm.entity_manager"
Catalyst\APIBundle\Command\UserCreateCommand:
arguments:
$em: "@doctrine.orm.entity_manager"
tags: ['console.command']
Catalyst\APIBundle\Command\TestCommand:
tags: ['console.command']
Catalyst\APIBundle\Command\TestAPICommand:
tags: ['console.command']
Catalyst\APIBundle\Access\Voter:
arguments:
$acl_gen: "@Catalyst\\APIBundle\\Access\\Generator"
$user_class: "Catalyst\\APIBundle\\Entity\\User"
tags: ['security.voter']
Catalyst\APIBundle\Access\Generator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
$acl_file: "%api_acl_file%"
Catalyst\MenuBundle\Menu\Generator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
Catalyst\MenuBundle\Listener\MenuAnnotationListener:
arguments:
$menu_name: "main_menu"
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
# invoice generator
App\Service\InvoiceGenerator\CMBInvoiceGenerator: ~

View file

@ -249,3 +249,7 @@ main_menu:
acl: ownership_type.menu
label: Ownership Types
parent: database
- id: customer_location_list
acl: cust_location.menu
label: Customer Locations
parent: database

View file

@ -0,0 +1,944 @@
catalyst_auth:
main:
user_entity: "App\\Entity\\User"
acl_data:
- id: dashboard
label: Dashboard Access
acls:
- id: dashboard.menu
label: Menu
- id: user
label: User Access
acls:
- id: user.menu
label: Menu
- id: user.list
label: List
- id: user.add
label: Add
- id: user.update
label: Update
- id: user.delete
label: Delete
- id: user.role.sadmin
label: Super Admin Role
- id: user.profile
label: User Profile
- id: role
label: Role Access
acls:
- id: role.menu
label: Menu
- id: role.list
label: List
- id: role.add
label: Add
- id: role.update
label: Update
- id: role.delete
label: Delete
- id: apiuser
label: API User Access
acls:
- id: apiuser.menu
label: Menu
- id: apiuser.list
label: List
- id: apiuser.add
label: Add
- id: apiuser.update
label: Update
- id: apiuser.delete
label: Delete
- id: apirole
label: API Role Access
acls:
- id: apirole.menu
label: Menu
- id: apirole.list
label: List
- id: apirole.add
label: Add
- id: apirole.update
label: Update
- id: apirole.delete
label: Delete
- id: logistics
label: Logistics Access
acls:
- id: logistics.menu
label: Menu
- id: battery
label: Battery Access
acls:
- id: battery.menu
label: Menu
- id: battery.list
label: List
- id: battery.add
label: Add
- id: battery.update
label: Update
- id: battery.delete
label: Delete
- id: bmfg
label: Battery Manufacturer Access
acls:
- id: bmfg.menu
label: Menu
- id: bmfg.list
label: List
- id: bmfg.add
label: Add
- id: bmfg.update
label: Update
- id: bmfg.delete
label: Delete
- id: bmodel
label: Battery Model Access
acls:
- id: bmodel.menu
label: Menu
- id: bmodel.list
label: List
- id: bmodel.add
label: Add
- id: bmodel.update
label: Update
- id: bmodel.delete
label: Delete
- id: bsize
label: Battery Size Access
acls:
- id: bsize.menu
label: Menu
- id: bsize.list
label: List
- id: bsize.add
label: Add
- id: bsize.update
label: Update
- id: bsize.delete
label: Delete
- id: vehicle
label: Vehicle Access
acls:
- id: vehicle.menu
label: Menu
- id: vehicle.list
label: List
- id: vehicle.add
label: Add
- id: vehicle.update
label: Update
- id: vehicle.delete
label: Delete
- id: vmfg
label: Vehicle Manufacturer Access
acls:
- id: vmfg.menu
label: Menu
- id: vmfg.list
label: List
- id: vmfg.add
label: Add
- id: vmfg.update
label: Update
- id: vmfg.delete
label: Delete
- id: customer
label: Customer Access
acls:
- id: customer.menu
label: Menu
- id: customer.list
label: List
- id: customer.add
label: Add
- id: customer.update
label: Update
- id: customer.delete
label: Delete
- id: customer.dpa
label: Display DPA
- id: location
label: Location Access
acls:
- id: location.menu
label: Menu
- id: outlet
label: Outlet Access
acls:
- id: outlet.menu
label: Menu
- id: outlet.list
label: List
- id: outlet.add
label: Add
- id: outlet.update
label: Update
- id: outlet.delete
label: Delete
- id: hub
label: Hub Access
acls:
- id: hub.menu
label: Menu
- id: hub.list
label: List
- id: hub.add
label: Add
- id: hub.update
label: Update
- id: hub.delete
label: Delete
- id: geofence
label: Geofence
acls:
- id: geofence.menu
label: Menu
- id: geofence.list
label: List
- id: geofence.add
label: Add
- id: geofence.delete
label: Delete
- id: rider
label: Rider Access
acls:
- id: rider.menu
label: Menu
- id: rider.list
label: List
- id: rider.add
label: Add
- id: rider.update
label: Update
- id: rider.delete
label: Delete
- id: servicecharge
label: Service Charge
acls:
- id: service_charge.menu
label: Menu
- id: service_charge.list
label: List
- id: service_charge.add
label: Add
- id: service_charge.update
label: Update
- id: service_charge.delete
label: Delete
- id: joborder
label: Job Order
acls:
- id: joborder.menu
label: Menu
- id: jo_in.list
label: Incoming
- id: jo_proc.list
label: Dispatch
- id: jo_proc.unlock
label: Dispatch Unlock
- id: jo_assign.list
label: Rider Assignment
- id: jo_assign.unlock
label: Rider Assignment Unlock
- id: jo_fulfill.list
label: Fulfillment
- id: jo_open.list
label: Open
- id: jo_all.list
label: View All
- id: jo_pdf.list
label: PDF
- id: jo_open.edit
label: Edit
- id: joborder.cancel
label: Cancel
- id: jo_onestep.form
label: One-step Process
- id: jo_onestep.edit
label: One-step Process Edit
- id: jo_walkin.form
label: Walk-in
- id: jo_walkin.edit
label: Walk-in Edit
- id: jo_autoassign.test
label: Autoassign Test
- id: jo_hub.list
label: Hub View
- id: jo_cancel.fulfill
label: Fulfill Cancelled JO
- id: jo_resq_proc.list
label: RESQ Dispatch
- id: jo_resq_all.list
label: RESQ All
- id: support
label: Customer Support Access
acls:
- id: support.menu
label: Menu
- id: general.search
label: Search
- id: warranty.search
label: Customer Battery Search
- id: warranty.upload
label: Warranty Upload
- id: ticket
label: Ticket Access
acls:
- id: ticket.menu
label: Menu
- id: ticket.list
label: List
- id: ticket.add
label: Add
- id: ticket.update
label: Update
- id: ticket.delete
label: Delete
- id: promo
label: Promo Access
acls:
- id: promo.menu
label: Menu
- id: promo.list
label: List
- id: promo.add
label: Add
- id: promo.update
label: Update
- id: promo.delete
label: Delete
- id: report
label: Reports
acls:
- id: report.menu
label: Menu
- id: report.reject
label: Rejection Report
- id: report.battery.conflict
label: Battery Conflict Report
- id: report.popapp.comparison
label: Popapp Comparison Report
- id: report.meh.customer
label: RESQ MEH Customer Report
- id: report.warranty.class
label: Warranty Class Report
- id: report.vehicle.battery.compatibility
label: Vehicle Battery Compatibility Report
- id: report.warranty.details
label: Warranty Details Report
- id: report.jo.details
label: Job Order Details Report
- id: report.jo_events
label: Job Order Events Report
- id: report.sms_messages
label: SMS Messages Report
- id: report.jo.auto_assign
label: Auto Assigned Job Order Report
- id: report.jo.advance_order
label: Advance Order Job Order Report
- id: report.customer.source
label: Customer Source Report
- id: report.hub.filter
label: Hub Filter Report
- id: report.warranty.raffle
label: Warranty Raffle Report
- id: report.jo.raffle
label: JO Raffle Report
- id: service
label: Other Services
acls:
- id: service.menu
label: Menu
- id: service.list
label: List
- id: service.add
label: Add
- id: service.update
label: Update
- id: service.delete
label: Delete
- id: partner
label: Partners
acls:
- id: partner.menu
label: Menu
- id: partner.list
label: List
- id: partner.add
label: Add
- id: partner.update
label: Update
- id: partner.delete
label: Delete
- id: motolite_event
label: Motolite Events
acls:
- id: motolite_event.menu
label: Menu
- id: motolite_event.list
label: List
- id: motolite_event.add
label: Add
- id: motolite_event.update
label: Update
- id: motolite_event.delete
label: Delete
- id: review
label: Reviews
acls:
- id: review.menu
label: Menu
- id: review.list
label: List
- id: review.view
label: View
- id: review.delete
label: Delete
- id: privacypolicy
label: Privacy Policy
acls:
- id: privacy_policy.menu
label: Menu
- id: privacy_policy.list
label: List
- id: privacy_policy.add
label: Add
- id: privacy_policy.update
label: Update
- id: privacy_policy.delete
label: Delete
- id: warranty
label: Warranty
acls:
- id: warranty.menu
label: Menu
- id: warranty.list
label: List
- id: warranty.add
label: Add
- id: warranty.update
label: Update
- id: staticcontent
label: Static Content
acls:
- id: static_content.menu
label: Menu
- id: static_content.list
label: List
- id: static_content.add
label: Add
- id: static_content.update
label: Update
- id: static_content.delete
label: Delete
- id: analytics
label: Analytics
acls:
- id: analytics.menu
label: Menu
- id: analytics.forecast
label: Forecasting
- id: sap_battery
label: SAP Battery Access
acls:
- id: sap_battery.menu
label: Menu
- id: sap_battery.list
label: List
- id: sap_battery.add
label: Add
- id: sap_battery.update
label: Update
- id: sap_battery.delete
label: Delete
- id: sap_brand
label: SAP Battery Brand Access
acls:
- id: sap_brand.menu
label: Menu
- id: sap_brand.list
label: List
- id: sap_brand.add
label: Add
- id: sap_brand.update
label: Update
- id: sap_brand.delete
label: Delete
- id: sap_bsize
label: SAP Battery Size Access
acls:
- id: sap_bsize.menu
label: Menu
- id: sap_bsize.list
label: List
- id: sap_bsize.add
label: Add
- id: sap_bsize.update
label: Update
- id: sap_bsize.delete
label: Delete
- id: sap_csize
label: SAP Battery Container Size Access
acls:
- id: sap_csize.menu
label: Menu
- id: sap_csize.list
label: List
- id: sap_csize.add
label: Add
- id: sap_csize.update
label: Update
- id: sap_csize.delete
label: Delete
- id: customer_tag
label: Customer Tags Access
acls:
- id: customer_tag.menu
label: Menu
- id: customer_tag.list
label: List
- id: customer_tag.add
label: Add
- id: customer_tag.update
label: Update
- id: customer_tag.delete
label: Delete
- id: review_tag
label: Review Tags Access
acls:
- id: review_tag.menu
label: Menu
- id: review_tag.list
label: List
- id: review_tag.add
label: Add
- id: review_tag.update
label: Update
- id: review_tag.delete
label: Delete
- id: dealer
label: Dealer Access
acls:
- id: dealer.menu
label: Menu
- id: dealer.list
label: List
- id: dealer.add
label: Add
- id: dealer.update
label: Update
- id: dealer.delete
label: Delete
- id: database
label: Database Access
acls:
- id: database.menu
label: Menu
- id: ticket_type
label: Ticket Type Access
acls:
- id: ticket_type.menu
label: Menu
- id: ticket_type.list
label: List
- id: ticket_type.add
label: Add
- id: ticket_type.update
label: Update
- id: ticket_type.delete
label: Delete
- id: subticket_type
label: Sub Ticket Type Access
acls:
- id: subticket_type.menu
label: Menu
- id: subticket_type.list
label: List
- id: subticket_type.add
label: Add
- id: subticket_type.update
label: Update
- id: subticket_type.delete
label: Delete
- id: emergency_type
label: Emergency Type Access
acls:
- id: emergency_type.menu
label: Menu
- id: emergency_type.list
label: List
- id: emergency_type.add
label: Add
- id: emergency_type.update
label: Update
- id: emergency_type.delete
label: Delete
- id: ownership_type
label: Ownership Type Access
acls:
- id: ownership_type.menu
label: Menu
- id: ownership_type.list
label: List
- id: ownership_type.add
label: Add
- id: ownership_type.update
label: Update
- id: ownership_type.delete
label: Delete
- id: service_offering
label: Service Offering Access
acls:
- id: service_offering.menu
label: Menu
- id: service_offering.list
label: List
- id: service_offering.add
label: Add
- id: service_offering.update
label: Update
- id: service_offering.delete
label: Delete
- id: price_tier
label: Price Tier
acls:
- id: price_tier.menu
label: Menu
- id: price_tier.list
label: List
- id: price_tier.add
label: Add
- id: price_tier.update
label: Update
- id: price_tier.delete
label: Delete
- id: item_type
label: Item Type
acls:
- id: item_type.menu
label: Menu
- id: item_type.list
label: List
- id: item_type.add
label: Add
- id: item_type.update
label: Update
- id: item_type.delete
label: Delete
- id: item
label: Item
acls:
- id: item.menu
label: Menu
- id: item_pricing
label: Item Pricing
acls:
- id: item_pricing.update
label: Update
api:
user_entity: "App\\Entity\\ApiUser"
acl_data:
- id: warranty
label: Warranty Access
acls:
- id: warranty.list
label: List
- id: warranty.find.serial
label: Find by Serial
- id: warranty.find.platenumber
label: Find by Plate Number
- id: warranty.register.battery
label: Register Battery
- id: warranty.claim
label: Claim
- id: warranty.update
label: Update
- id: warranty.cancel
label: Cancel
- id: warranty.delete
label: Delete
- id: warranty.set.privacypolicy
label: Set Privacy Policy
- id: warranty.list.serial
label: List by Serial
- id: batterybrand
label: Battery Brand Access
acls:
- id: batterybrand.list
label: List
- id: batterysize
label: Battery Size Access
acls:
- id: batterysize.list
label: List
- id: battery
label: Battery Access
acls:
- id: battery.list
label: List
- id: vmanufacturer
label: Vehicle Manufacturer Access
acls:
- id: vmanufacturer.list
label: List
- id: vehicle
label: Vehicle Access
acls:
- id: vehicle.list
label: List
- id: privacypolicy
label: Privacy Policy
acls:
- id: privacypolicy.find
label: Find Privacy Policy
- id: customer
label: Customer
acls:
- id: customer.register
label: Register Customer
- id: customer.verify
label: Verify Customer
- id: municipality
label: Municipality
acls:
- id: municipality.list
label: List
- id: dealer
label: Dealer
acls:
- id: dealer.list
label: List
- id: warrantyserial
label: Warranty Serial
acls:
- id: warrantyserial.upload
label: Upload
- id: hub
label: Hub Access
acls:
- id: hub.list
label: List
- id: joborder
label: Job Order Access
acls:
- id: joborder.find
label: Find Job Order
- id: tapi_vmanufacturer
label: Third Party Vehicle Manufacturer Access
acls:
- id: tapi_vmanufacturer.list
label: List Third Party Vehicle Manufacturers
- id: tapi_vehicle
label: Third Party Vehicle Make Access
acls:
- id: tapi_vehicle.list
label: List Third Party Vehicles
- id: tapi_promo
label: Third Party Promo Access
acls:
- id: tapi_promo.list
label: List Third Party Promos
- id: tapi_battery
label: Third Party Battery Access
acls:
- id: tapi_battery_compatible.list
label: List Third Party Compatible Batteries
- id: tapi_jo
label: Third Party Job Order Access
acls:
- id: tapi_jo.request
label: Third Party Request Job Order
- id: tapi_jo.get.estimate
label: Third Party Get Estimate
- id: tapi_jo.get.ongoing
label: Third Party Get Ongoing Job Order
- id: tapi_jo.cancel
label: Third Party Cancel Job Order
- id: tapi_jo.get.invoice
label: Third Party Get Job Order Invoice
- id: tapi_jo.location.support
label: Third Party Check Location Support
- id: tapi_jo.nearest_hub.get
label: Third Party Get Nearest Hub and Slots
- id: tapi_jo.schedule_option.status
label: Third Party Schedule Option Status
- id: tapi_jo.get.info
label: Third Party Get Job Order Info
- id: tapi_service
label: Third Party Service Access
acls:
- id: tapi_service.list
label: List Third Party Services
cust_api_v2:
user_entity: "App\\Entity\\CustomerUser"
acl_data:
- id: cust_api_v2.auth
label: Authentication
acls:
- id: cust_api_v2.auth.register
label: Register
- id: cust_api_v2.auth.confirm
label: Confirm Number
- id: cust_api_v2.auth.validate
label: Validate Code
- id: cust_api_v2.auth.resend_code
label: Resend Code
- id: cust_api_v2.customer
label: Customer
acls:
- id: cust_api_v2.customer.info
label: Info
- id: cust_api_v2.customer.update
label: Update
- id: cust_api_v2.customer.status
label: Status
- id: cust_api_v2.customer.hash
label: Hash
- id: cust_api_v2.device
label: Device
acls:
- id: cust_api_v2.device.id
label: Update
- id: cust_api_v2.invoice
label: Invoice
acls:
- id: cust_api_v2.invoice.estimate
label: Estimate
- id: cust_api_v2.jo
label: Job Order
acls:
- id: cust_api_v2.jo.ongoing
label: List Ongoing
- id: cust_api_v2.jo.invoice
label: Get Invoice
- id: cust_api_v2.jo.cancel
label: Cancel
- id: cust_api_v2.jo.info
label: Info
- id: cust_api_v2.jo.history
label: History
- id: cust_api_v2.jo.latest
label: Latest
- id: cust_api_v2.jo.all_ongoing
label: List All Ongoing
- id: cust_api_v2.jo.ongoing_count
label: List Ongoing Count
- id: cust_api_v2.jo.create
label: Create
- id: cust_api_v2.jo.request
label: Request
- id: cust_api_v2.jo.completed
label: List Completed
- id: cust_api_v2.location
label: Location
acls:
- id: cust_api_v2.location.support
label: Get Support Status
- id: cust_api_v2.location.nearest_hub_and_slots
label: List Nearest Hub and Slots
- id: cust_api_v2.location.create
label: Create
- id: cust_api_v2.location.list
label: List
- id: cust_api_v2.partner
label: Partner
acls:
- id: cust_api_v2.partner.info
label: Info
- id: cust_api_v2.partner.closest
label: List Closest Partners
- id: cust_api_v2.partner.review
label: Review
- id: cust_api_v2.privacy
label: Privacy
acls:
- id: cust_api_v2.privacy.settings
label: Get Privacy Settings
- id: cust_api_v2.promo
label: Promo
acls:
- id: cust_api_v2.promo.list
label: Get Promos
- id: cust_api_v2.rider
label: Rider
acls:
- id: cust_api_v2.rider.status
label: Status
- id: cust_api_v2.rider.rating
label: Rate
- id: cust_api_v2.schedule
label: Schedule
acls:
- id: cust_api_v2.schedule.status
label: Get Schedule Option Status
- id: cust_api_v2.service
label: Service
acls:
- id: cust_api_v2.service.list
label: List
- id: cust_api_v2.vehicle
label: Vehicle
acls:
- id: cust_api_v2.vehicle.mfgs
label: List Manufacturers
- id: cust_api_v2.vehicle.makes
label: List Makes
- id: cust_api_v2.vehicle.create
label: Add
- id: cust_api_v2.vehicle.update
label: Update
- id: cust_api_v2.vehicle.list
label: List
- id: cust_api_v2.vehicle.batteries
label: Compatible Batteries
- id: cust_api_v2.vehicle.delete
label: Delete
- id: cust_api_v2.warranty
label: Warranty
acls:
- id: cust_api_v2.warranty.activate
label: Activate
- id: cust_api_v2.warranty.check
label: Check Status
- id: cust_api_v2.warranty.register
label: Register

View file

@ -0,0 +1,308 @@
catalyst_menu:
main:
- id: home
acl: dashboard.menu
label: '[menu.dashboard]'
icon: flaticon-line-graph
order: 1
- id: user
acl: user.menu
label: '[menu.user]'
icon: flaticon-users
order: 2
- id: user_list
acl: user.list
label: '[menu.user.users]'
parent: user
- id: role_list
acl: role.list
label: '[menu.user.roles]'
parent: user
- id: apiuser
acl: apiuser.menu
label: '[menu.apiuser]'
icon: flaticon-users
order: 3
- id: api_user_list
acl: apiuser.list
label: '[menu.apiuser.users]'
parent: apiuser
- id: api_role_list
acl: apirole.list
label: '[menu.apiuser.roles]'
parent: apiuser
- id: logistics
acl: logistics.menu
label: '[menu.logistics]'
icon: fa fa-truck
order: 4
- id: rider_list
acl: rider.list
label: '[menu.logistics.riders]'
parent: logistics
- id: battery
acl: battery.menu
label: '[menu.battery]'
icon: fa fa-battery-3
order: 5
- id: battery_list
acl: battery.list
label: '[menu.battery.batteries]'
parent: battery
- id: bmfg_list
acl: bmfg.list
label: '[menu.battery.manufacturers]'
parent: battery
- id: bmodel_list
acl: bmodel.list
label: '[menu.battery.models]'
parent: battery
- id: bsize_list
acl: bsize.list
label: '[menu.battery.sizes]'
parent: battery
- id: promo_list
acl: promo.list
label: '[menu.battery.promos]'
parent: battery
- id: sapbattery
acl: sap_battery.menu
label: '[menu.sapbattery]'
icon: fa fa-battery
order: 6
- id: sapbattery_list
acl: sap_battery.list
label: '[menu.sapbattery.batteries]'
parent: sapbattery
- id: sapbrand_list
acl: sap_brand.list
label: '[menu.sapbattery.brands]'
parent: sapbattery
- id: sapbsize_list
acl: sap_bsize.list
label: '[menu.sapbattery.sizes]'
parent: sapbattery
- id: sapcsize_list
acl: sap_csize.list
label: '[menu.sapbattery.csizes]'
parent: sapbattery
- id: vehicle
acl: vehicle.menu
label: '[menu.vehicle]'
icon: fa fa-car
order: 7
- id: vehicle_list
acl: vehicle.list
label: '[menu.vehicle.vehicles]'
parent: vehicle
- id: vmfg_list
acl: vmfg.list
label: '[menu.vehicle.manufacturers]'
parent: vehicle
- id: location
acl: location.menu
label: '[menu.location]'
icon: fa fa-home
order: 8
- id: outlet_list
acl: outlet.menu
label: '[menu.location.outlets]'
parent: location
- id: hub_list
acl: hub.menu
label: '[menu.location.hubs]'
parent: location
- id: dealer_list
acl: dealer.list
label: '[menu.location.dealers]'
parent: location
- id: geofence_list
acl: geofence.menu
label: '[menu.location.geofence]'
parent: location
- id: joborder
acl: joborder.menu
label: '[menu.joborder]'
icon: flaticon-calendar-3
order: 9
- id: jo_in
acl: jo_in.list
label: '[menu.joborder.incoming]'
parent: joborder
- id: jo_proc
acl: jo_proc.list
label: '[menu.joborder.dispatch]'
parent: joborder
- id: jo_resq_proc
acl: jo_resq_proc.list
label: '[menu.joborder.resqdispatch]'
parent: joborder
- id: jo_assign
acl: jo_assign.list
label: '[menu.joborder.assignment]'
parent: joborder
- id: jo_fulfill
acl: jo_fulfill.list
label: '[menu.joborder.fulfillment]'
parent: joborder
- id: jo_open
acl: jo_open.list
label: '[menu.joborder.open]'
parent: joborder
- id: jo_all
acl: jo_all.list
label: '[menu.joborder.viewall]'
parent: joborder
- id: jo_hub_view
acl: jo_hub.list
label: '[menu.joborder.hubview]'
parent: joborder
- id: jo_resq_all
acl: jo_resq_all.list
label: '[menu.joborder.resqall]'
parent: joborder
- id: support
acl: support.menu
label: '[menu.support]'
icon: flaticon-support
order: 11
- id: customer_list
acl: customer.list
label: '[menu.support.customers]'
parent: support
- id: ticket_list
acl: ticket.list
label: '[menu.support.tickets]'
parent: support
- id: general_search
acl: general.search
label: '[menu.support.search]'
parent: support
- id: warranty_search
acl: warranty.search
label: '[menu.support.warrantysearch]'
parent: support
- id: privacy_policy_list
acl: privacy_policy.list
label: '[menu.support.privacypolicy]'
parent: support
- id: warranty_list
acl: warranty.list
label: '[menu.support.warranty]'
parent: support
- id: warranty_upload
acl: warranty.upload
label: '[menu.support.warrantyupload]'
parent: support
- id: static_content_list
acl: static_content.list
label: '[menu.support.staticcontent]'
parent: support
- id: customertag_list
acl: customer_tag.list
label: '[menu.support.customertags]'
parent: support
- id: reviewtag_list
acl: review_tag.list
label: '[menu.support.reviewtags]'
parent: support
- id: service
acl: service.menu
label: '[menu.service]'
icon: flaticon-squares
order: 12
- id: service_list
acl: service.list
label: '[menu.service.services]'
parent: service
- id: partner
acl: partner.menu
label: '[menu.partner]'
icon: flaticon-network
order: 13
- id: partner_list
acl: partner.list
label: '[menu.partner.partners]'
parent: partner
- id: review_list
acl: review.list
label: '[menu.partner.reviews]'
parent: partner
- id: motolite_event
acl: motolite_event.menu
label: '[menu.motolite_event]'
icon: flaticon-event-calendar-symbol
order: 14
- id: motolite_event_list
acl: motolite_event.list
label: '[menu.motolite_event.events]'
parent: motolite_event
- id: analytics
acl: analytics.menu
label: '[menu.analytics]'
icon: flaticon-graphic
order: 15
- id: analytics_forecast_form
acl: analytics.forecast
label: '[menu.analytics.forecasting]'
parent: analytics
- id: database
acl: database.menu
label: '[menu.database]'
icon: fa fa-database
order: 16
- id: ticket_type_list
acl: ticket_type.menu
label: '[menu.database.tickettypes]'
parent: database
- id: subticket_type_list
acl: subticket_type.menu
label: '[menu.database.subtickettypes]'
parent: database
- id: emergency_type_list
acl: emergency_type.menu
label: '[menu.database.emergencytypes]'
parent: database
- id: ownership_type_list
acl: ownership_type.menu
label: '[menu.database.ownershiptypes]'
parent: database
- id: service_offering_list
acl: service_offering.menu
label: '[menu.database.serviceofferings]'
parent: database
- id: item_type_list
acl: item_type.menu
label: '[menu.database.itemtypes]'
parent: database
- id: item
acl: item.menu
label: Item Management
icon: fa fa-boxes
order: 10
- id: price_tier_list
acl: price_tier.list
label: Price Tiers
parent: item
- id: item_pricing
acl: item_pricing.update
label: Item Pricing
parent: item

View file

@ -9,8 +9,14 @@ security:
entity:
class: App\Entity\User
property: username
api_key_user_provider:
id: Catalyst\APIBundle\Security\APIKeyUserProvider
api_provider:
entity:
class: App\Entity\ApiUser
property: api_key
api_v2_provider:
entity:
class: App\Entity\CustomerUser
property: api_key
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
@ -47,29 +53,53 @@ security:
pattern: ^\/test_capi\/
security: false
insurance:
pattern: ^\/insurance\/
security: false
paymongo:
pattern: ^\/paymongo\/
security: false
cust_api_v2:
pattern: ^\/apiv2\/(?!register|register\/|number_confirm|number_confirm\/|code_validate|code_validate\/|resend_code|resend_code\/|version_check|version_check\/|account|account\/|account_code_validate|account_code_validate\/|account_resend_code|account_resend_code\/)
provider: api_v2_provider
access_denied_handler: Catalyst\ApiBundle\Service\AccessDeniedHandler
stateless: true
guard:
authenticators:
- Catalyst\ApiBundle\Security\Authenticator
cust_api_v2_guest:
pattern: ^\/apiv2\/(register|register\/|number_confirm|number_confirm\/|code_validate|code_validate\/|resend_code|resend_code\/|version_check|version_check\/|account|account\/|account_code_validate|account_code_validate\/|account_resend_code|account_resend_code\/)
security: false
warranty_api:
pattern: ^\/capi\/
provider: api_provider
access_denied_handler: Catalyst\ApiBundle\Service\AccessDeniedHandler
stateless: true
simple_preauth:
authenticator: Catalyst\APIBundle\Security\APIKeyAuthenticator
provider: api_key_user_provider
user_checker: Catalyst\AuthBundle\Service\UserChecker
guard:
authenticators:
- Catalyst\ApiBundle\Security\Authenticator
new_rider_api:
pattern: ^\/rider_api\/
provider: api_provider
access_denied_handler: Catalyst\ApiBundle\Service\AccessDeniedHandler
stateless: true
simple_preauth:
authenticator: Catalyst\APIBundle\Security\APIKeyAuthenticator
provider: api_key_user_provider
user_checker: Catalyst\AuthBundle\Service\UserChecker
guard:
authenticators:
- Catalyst\ApiBundle\Security\Authenticator
third_party_api:
pattern: ^\/tapi\/
provider: api_provider
access_denied_handler: Catalyst\ApiBundle\Service\AccessDeniedHandler
stateless: true
simple_preauth:
authenticator: Catalyst\APIBundle\Security\APIKeyAuthenticator
provider: api_key_user_provider
user_checker: Catalyst\AuthBundle\Service\UserChecker
guard:
authenticators:
- Catalyst\ApiBundle\Security\Authenticator
main:
provider: user_provider
@ -84,6 +114,7 @@ security:
lifetime: 604800
path: /
user_checker: Catalyst\AuthBundle\Service\UserChecker
switch_user: { role: ROLE_SUPER_ADMIN }
# activate different ways to authenticate

View file

@ -43,20 +43,6 @@ services:
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
Catalyst\AuthBundle\Service\ACLGenerator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
$acl_file: "%app_acl_file%"
Catalyst\AuthBundle\Service\ACLVoter:
arguments:
$user_class: "App\\Entity\\User"
tags: ['security.voter']
Catalyst\AuthBundle\Service\UserChecker:
App\Service\FileUploader:
arguments:
$target_dir: '%image_upload_directory%'
@ -115,50 +101,6 @@ services:
arguments:
$redis_client: "@App\\Service\\RedisClientProvider"
Catalyst\APIBundle\Security\APIKeyUserProvider:
arguments:
$em: "@doctrine.orm.entity_manager"
Catalyst\APIBundle\Security\APIKeyAuthenticator:
arguments:
$em: "@doctrine.orm.entity_manager"
Catalyst\APIBundle\Command\UserCreateCommand:
arguments:
$em: "@doctrine.orm.entity_manager"
tags: ['console.command']
Catalyst\APIBundle\Command\TestCommand:
tags: ['console.command']
Catalyst\APIBundle\Command\TestAPICommand:
tags: ['console.command']
Catalyst\APIBundle\Access\Voter:
arguments:
$acl_gen: "@Catalyst\\APIBundle\\Access\\Generator"
$user_class: "Catalyst\\APIBundle\\Entity\\User"
tags: ['security.voter']
Catalyst\APIBundle\Access\Generator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
$acl_file: "%api_acl_file%"
Catalyst\MenuBundle\Menu\Generator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
Catalyst\MenuBundle\Listener\MenuAnnotationListener:
arguments:
$menu_name: "main_menu"
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
# invoice generator
App\Service\InvoiceGenerator\ResqInvoiceGenerator: ~

315
config/routes/apiv2.yaml Normal file
View file

@ -0,0 +1,315 @@
# api
apiv2_register:
path: /apiv2/register
controller: App\Controller\CustomerAppAPI\AuthController::register
methods: [POST]
apiv2_confirm:
path: /apiv2/number_confirm
controller: App\Controller\CustomerAppAPI\AuthController::confirmNumber
methods: [POST]
apiv2_validate:
path: /apiv2/code_validate
controller: App\Controller\CustomerAppAPI\AuthController::validateCode
methods: [POST]
apiv2_info_get:
path: /apiv2/info
controller: App\Controller\CustomerAppAPI\CustomerController::getInfo
methods: [GET]
apiv2_info_update:
path: /apiv2/info
controller: App\Controller\CustomerAppAPI\CustomerController::updateInfo
methods: [POST]
apiv2_status:
path: /apiv2/status
controller: App\Controller\CustomerAppAPI\CustomerController::getStatus
methods: [GET]
apiv2_vehicle_mfg_list:
path: /apiv2/vehicle/mfgs
controller: App\Controller\CustomerAppAPI\VehicleController::listVehicleManufacturers
methods: [GET]
apiv2_vehicle_make_list:
path: /apiv2/vehicle/mfgs/{mfg_id}/makes
controller: App\Controller\CustomerAppAPI\VehicleController::listVehicleMakes
methods: [GET]
apiv2_cust_vehicle_add:
path: /apiv2/vehicles
controller: App\Controller\CustomerAppAPI\VehicleController::addVehicle
methods: [POST]
apiv2_cust_vehicle_info:
path: /apiv2/vehicles/{id}
controller: App\Controller\CustomerAppAPI\VehicleController::getVehicle
methods: [GET]
apiv2_cust_vehicle_update:
path: /apiv2/vehicles/{id}
controller: App\Controller\CustomerAppAPI\VehicleController::updateVehicle
methods: [POST]
apiv2_cust_vehicle_list:
path: /apiv2/vehicles
controller: App\Controller\CustomerAppAPI\VehicleController::listVehicles
methods: [GET]
apiv2_promo_list:
path: /apiv2/promos
controller: App\Controller\CustomerAppAPI\PromoController::listPromos
methods: [GET]
apiv2_battery_list:
path: /apiv2/vehicles/{vid}/compatible_batteries
controller: App\Controller\CustomerAppAPI\VehicleController::getCompatibleBatteries
methods: [GET]
apiv2_jo_request:
path: /apiv2/job_order
controller: App\Controller\CustomerAppAPI\JobOrderController::requestJobOrder
methods: [POST]
apiv2_estimate:
path: /apiv2/estimate
controller: App\Controller\CustomerAppAPI\InvoiceController::getEstimate
methods: [POST]
apiv2_ongoing:
path: /apiv2/job_order/ongoing
controller: App\Controller\CustomerAppAPI\JobOrderController::getOngoing
methods: [GET]
apiv2_rider_status:
path: /apiv2/rider
controller: App\Controller\CustomerAppAPI\RiderController::getRiderStatus
methods: [GET]
apiv2_rider_rating_add:
path: /apiv2/rider_rating
controller: App\Controller\CustomerAppAPI\RiderController::addRiderRating
methods: [POST]
apiv2_jo_cancel:
path: /apiv2/job_order/cancel
controller: App\Controller\CustomerAppAPI\JobOrderController:cancelJobOrder
methods: [POST]
apiv2_jo_history:
path: /apiv2/job_order/history
controller: App\Controller\CustomerAppAPI\JobOrderController:getJOHistory
methods: [GET]
apiv2_jo_invoice:
path: /apiv2/job_order/invoice
controller: App\Controller\CustomerAppAPI\JobOrderController:getJOInvoice
methods: [GET]
apiv2_device_id:
path: /apiv2/device_id
controller: App\Controller\CustomerAppAPI\DeviceController:updateDeviceID
methods: [POST]
apiv2_privacy:
path: /apiv2/privacy
controller: App\Controller\CustomerAppAPI\PrivacyController:privacySettings
methods: [POST]
apiv2_resend_code:
path: /apiv2/resend_code
controller: App\Controller\CustomerAppAPI\AuthController:resendCode
methods: [POST]
apiv2_location_support:
path: /apiv2/location_support
controller: App\Controller\CustomerAppAPI\LocationController:locationSupport
methods: [GET]
apiv2_activate_warranty:
path: /apiv2/activate_warranty
controller: App\Controller\CustomerAppAPI\WarrantyController:activateWarranty
methods: [POST]
apiv2_service_list:
path: /apiv2/services
controller: App\Controller\CustomerAppAPI\ServiceController:listServices
methods: [GET]
apiv2_partner_info:
path: /apiv2/partners/{pid}
controller: App\Controller\CustomerAppAPI\PartnerController:getPartnerInformation
methods: [GET]
apiv2_partner:
path: /apiv2/partners
controller: App\Controller\CustomerAppAPI\PartnerController:getClosestPartners
methods: [GET]
apiv2_partner_review:
path: /apiv2/partners/{pid}/review
controller: App\Controller\CustomerAppAPI\PartnerController:reviewPartner
methods: [POST]
apiv2_nearest_hub_slots:
path: /apiv2/hub_slots
controller: App\Controller\CustomerAppAPI\LocationController::getNearestHubAndSlots
methods: [GET]
apiv2_new_jo_request:
path: /apiv2/new_job_order
controller: App\Controller\CustomerAppAPI\JobOrderController::newRequestJobOrder
methods: [POST]
apiv2_version_check:
path: /apiv2/version_check
controller: App\Controller\CustomerAppAPI\AppController::versionCheck
methods: [GET]
apiv2_schedule_option_status:
path: /apiv2/schedule_option_status
controller: App\Controller\CustomerAppAPI\ScheduleController::scheduleOptionStatus
methods: [GET]
# paperless warranty / qr code
apiv2_warr_serial_check:
path: /apiv2/warranty/{serial}
controller: App\Controller\CustomerAppAPI\WarrantyController::warrantyCheck
methods: [GET]
apiv2_warr_serial_register:
path: /apiv2/warranty/{serial}
controller: App\Controller\CustomerAppAPI\WarrantyController::warrantyRegister
methods: [POST]
apiv2_jo_info:
path: /apiv2/job_order/{id}/info
controller: App\Controller\CustomerAppAPI\JobOrderController::getJobOrderInfo
methods: [GET]
apiv2_ongoing_job_orders:
path: /apiv2/job_orders/ongoing
controller: App\Controller\CustomerAppAPI\JobOrderController::getAllOngoingJobOrders
methods: [GET]
apiv2_ongoing_jo_count:
path: /apiv2/job_orders/ongoing/count
controller: App\Controller\CustomerAppAPI\JobOrderController::getOngoingJobOrderCount
methods: [GET]
apiv2_new_location:
path: /apiv2/new_location
controller: App\Controller\CustomerAppAPI\LocationController::addLocation
methods: [POST]
apiv2_locations:
path: /apiv2/locations
controller: App\Controller\CustomerAppAPI\LocationController::getLocations
methods: [GET]
apiv2_location_remove:
path: /apiv2/locations/{id}/remove
controller: App\Controller\CustomerAppAPI\LocationController::removeLocation
methods: [POST]
apiv2_cust_vehicle_remove:
path: /apiv2/vehicles/{id}/remove
controller: App\Controller\CustomerAppAPI\VehicleController::removeVehicle
methods: [POST]
apiv2_latest_job_order:
path: /apiv2/job_order/latest
controller: App\Controller\CustomerAppAPI\JobOrderController::getLatestJobOrder
methods: [GET]
apiv2_customer_hash_get:
path: /apiv2/customer_hash
controller: App\Controller\CustomerAppAPI\CustomerController::getCustomerHash
methods: [GET]
#apiv2_completed_job_orders:
# path: /apiv2/job_orders/completed
# controller: App\Controller\CustomerAppAPI\JobOrderController::getCompletedJobOrders
# methods: [GET]
# motolite events
apiv2_motolite_events:
path: /apiv2/motolite_events
controller: App\Controller\CustomerAppAPI\MotoliteEventController::getEvents
methods: [GET]
# review tags
apiv2_partner_review_tags:
path: /apiv2/review_tags/partner
controller: App\Controller\CustomerAppAPI\ReviewTagController::getPartnerReviewTags
apiv2_rider_review_tags:
path: /apiv2/review_tags/rider
controller: App\Controller\CustomerAppAPI\ReviewTagController::getRiderReviewTags
# account deletion
apiv2_account_delete:
path: /apiv2/account_delete
controller: App\Controller\CustomerAppAPI\AccountController::deleteAccount
methods: [POST]
apiv2_account_delete_resend_code:
path: /apiv2/account_delete_resend_code
controller: App\Controller\CustomerAppAPI\AccountController:resendCode
methods: [POST]
apiv2_account_delete_code_validate:
path: /apiv2/account_delete_code_validate
controller: App\Controller\CustomerAppAPI\AccountController::validateDeleteCode
methods: [POST]
# trade-in support
apiv2_cust_vehicle_trade_in_estimate:
path: /apiv2/vehicles/{id}/trade_in_estimate
controller: App\Controller\CustomerAppAPI\VehicleController::getTradeInEstimate
methods: [GET]
# insurance
apiv2_insurance_vehicle_maker_list:
path: /apiv2/insurance/vehicles/makers
controller: App\Controller\CustomerAppAPI\InsuranceController::getVehicleMakers
methods: [GET]
apiv2_insurance_vehicle_model_list:
path: /apiv2/insurance/vehicles/models/{maker_id}
controller: App\Controller\CustomerAppAPI\InsuranceController::getVehicleModels
methods: [GET]
apiv2_insurance_vehicle_trim_list:
path: /apiv2/insurance/vehicles/trims/{model_id}
controller: App\Controller\CustomerAppAPI\InsuranceController::getVehicleTrims
methods: [GET]
apiv2_insurance_vehicle_mv_type_list:
path: /apiv2/insurance/mvtypes
controller: App\Controller\CustomerAppAPI\InsuranceController::getMVTypes
methods: [GET]
apiv2_insurance_vehicle_client_type_list:
path: /apiv2/insurance/clienttypes
controller: App\Controller\CustomerAppAPI\InsuranceController::getClientTypes
methods: [GET]
apiv2_insurance_application_create:
path: /apiv2/insurance/application
controller: App\Controller\CustomerAppAPI\InsuranceController::createApplication
methods: [POST]
apiv2_insurance_premiums_banner:
path: /apiv2/insurance/premiums_banner
controller: App\Controller\CustomerAppAPI\InsuranceController::getPremiumsBanner
methods: [GET]
apiv2_insurance_body_types:
path: /apiv2/insurance/body_types
controller: App\Controller\CustomerAppAPI\InsuranceController::getBodyTypes
methods: [GET]

View file

@ -194,3 +194,16 @@ capi_warranty_serial_upload:
path: /capi/warranty_serial/upload
controller: App\Controller\CAPI\WarrantySerialController::uploadWarrantySerialFile
methods: [POST]
# pullout form system
# hub
capi_hub_list:
path: /capi/hubs
controller: App\Controller\CAPI\HubController::getAll
methods: [GET]
# job order details
capi_job_order:
path: /capi/job_order/{id}
controller: App\Controller\CAPI\JobOrderController::getJobOrder
methods: [GET]

View file

@ -94,3 +94,24 @@ capi_rider_jo_start:
path: /rider_api/start
controller: App\Controller\CAPI\RiderAppController::startJobOrder
methods: [POST]
# trade-ins
capi_rider_battery_sizes:
path: /rider_api/battery_sizes
controller: App\Controller\CAPI\RiderAppController::getBatterySizes
methods: [GET]
capi_rider_trade_in_types:
path: /rider_api/trade_in_types
controller: App\Controller\CAPI\RiderAppController::getTradeInTypes
methods: [GET]
capi_rider_battery_info:
path: /rider_api/battery/{serial}
controller: App\Controller\CAPI\RiderAppController::getBatteryInfo
methods: [GET]
capi_rider_update_jo:
path: /rider_api/job_order/update
controller: App\Controller\CAPI\RiderAppController::updateJobOrder
methods: [POST]

View file

@ -0,0 +1,227 @@
# api
cust_api_register:
path: /apiv2/register
controller: App\Controller\CustomerAppAPI\AuthController::register
methods: [POST]
cust_api_confirm:
path: /apiv2/number_confirm
controller: App\Controller\CustomerAppAPI\AuthController::confirmNumber
methods: [POST]
cust_api_validate:
path: /apiv2/code_validate
controller: App\Controller\CustomerAppAPI\AuthController::validateCode
methods: [POST]
cust_api_info_get:
path: /apiv2/info
controller: App\Controller\CustomerAppAPI\CustomerController::getInfo
methods: [GET]
cust_api_info_update:
path: /apiv2/info
controller: App\Controller\CustomerAppAPI\CustomerController::updateInfo
methods: [POST]
cust_api_status:
path: /apiv2/status
controller: App\Controller\CustomerAppAPI\CustomerController::getStatus
methods: [GET]
cust_api_vehicle_mfg_list:
path: /apiv2/vehicle/mfgs
controller: App\Controller\CustomerAppAPI\VehicleController::listVehicleManufacturers
methods: [GET]
cust_api_vehicle_make_list:
path: /apiv2/vehicle/mfgs/{mfg_id}/makes
controller: App\Controller\CustomerAppAPI\VehicleController::listVehicleMakes
methods: [GET]
cust_api_cust_vehicle_add:
path: /apiv2/vehicles
controller: App\Controller\CustomerAppAPI\VehicleController::addVehicle
methods: [POST]
cust_api_cust_vehicle_update:
path: /apiv2/vehicles/{id}
controller: App\Controller\CustomerAppAPI\VehicleController::updateVehicle
methods: [POST]
cust_api_cust_vehicle_list:
path: /apiv2/vehicles
controller: App\Controller\CustomerAppAPI\VehicleController::listVehicles
methods: [GET]
cust_api_promo_list:
path: /apiv2/promos
controller: App\Controller\CustomerAppAPI\PromoController::listPromos
methods: [GET]
cust_api_battery_list:
path: /apiv2/vehicles/{vid}/compatible_batteries
controller: App\Controller\CustomerAppAPI\VehicleController::getCompatibleBatteries
methods: [GET]
cust_api_jo_request:
path: /apiv2/job_order
controller: App\Controller\CustomerAppAPI\JobOrderController::requestJobOrder
methods: [POST]
cust_api_estimate:
path: /apiv2/estimate
controller: App\Controller\CustomerAppAPI\EstimateController::getEstimate
methods: [POST]
cust_api_ongoing:
path: /apiv2/job_order/ongoing
controller: App\Controller\CustomerAppAPI\JobOrderController::getOngoing
methods: [GET]
cust_api_rider_status:
path: /apiv2/rider
controller: App\Controller\CustomerAppAPI\RiderController::getRiderStatus
methods: [GET]
cust_api_rider_rating_add:
path: /apiv2/rider_rating
controller: App\Controller\CustomerAppAPI\RiderController::addRiderRating
methods: [POST]
cust_api_jo_cancel:
path: /apiv2/job_order/cancel
controller: App\Controller\CustomerAppAPI\JobOrderController:cancelJobOrder
methods: [POST]
cust_api_jo_history:
path: /apiv2/job_order/history
controller: App\Controller\CustomerAppAPI\JobOrderController:getJOHistory
methods: [GET]
cust_api_jo_invoice:
path: /apiv2/job_order/invoice
controller: App\Controller\CustomerAppAPI\JobOrderController:getJOInvoice
methods: [GET]
cust_api_device_id:
path: /apiv2/device_id
controller: App\Controller\CustomerAppAPI\DeviceController:updateDeviceID
methods: [POST]
cust_api_privacy:
path: /apiv2/privacy
controller: App\Controller\CustomerAppAPI\PrivacyController:privacySettings
methods: [POST]
cust_api_resend_code:
path: /apiv2/resend_code
controller: App\Controller\CustomerAppAPI\AuthController:resendCode
methods: [POST]
cust_api_location_support:
path: /apiv2/location_support
controller: App\Controller\CustomerAppAPI\LocationController:locationSupport
methods: [GET]
cust_api_activate_warranty:
path: /apiv2/activate_warranty
controller: App\Controller\CustomerAppAPI\WarrantyController:activateWarranty
methods: [POST]
cust_api_service_list:
path: /apiv2/services
controller: App\Controller\CustomerAppAPI\ServiceController:listServices
methods: [GET]
cust_api_partner_info:
path: /apiv2/partners/{pid}
controller: App\Controller\CustomerAppAPI\PartnerController:getPartnerInformation
methods: [GET]
cust_api_partner:
path: /apiv2/partners
controller: App\Controller\CustomerAppAPI\PartnerController:getClosestPartners
methods: [GET]
cust_api_partner_review:
path: /apiv2/partners/{pid}/review
controller: App\Controller\CustomerAppAPI\PartnerController:reviewPartner
methods: [POST]
cust_api_nearest_hub_slots:
path: /apiv2/hub_slots
controller: App\Controller\CustomerAppAPI\LocationController::getNearestHubAndSlots
methods: [GET]
cust_api_new_jo_request:
path: /apiv2/new_job_order
controller: App\Controller\CustomerAppAPI\JobOrderController::newRequestJobOrder
methods: [POST]
cust_api_version_check:
path: /apiv2/version_check
controller: App\Controller\CustomerAppAPI\AppController::versionCheck
methods: [GET]
cust_api_schedule_option_status:
path: /apiv2/schedule_option_status
controller: App\Controller\CustomerAppAPI\ScheduleController::scheduleOptionStatus
methods: [GET]
# paperless warranty / qr code
cust_api_warr_serial_check:
path: /apiv2/warranty/{serial}
controller: App\Controller\CustomerAppAPI\WarrantyController::warrantyCheck
methods: [GET]
cust_api_warr_serial_register:
path: /apiv2/warranty/{serial}
controller: App\Controller\CustomerAppAPI\WarrantyController::warrantyRegister
methods: [POST]
cust_api_jo_info:
path: /apiv2/job_order/{id}/info
controller: App\Controller\CustomerAppAPI\JobOrderController::getJobOrderInfo
methods: [GET]
cust_api_ongoing_job_orders:
path: /apiv2/job_orders/ongoing
controller: App\Controller\CustomerAppAPI\JobOrderController::getAllOngoingJobOrders
methods: [GET]
cust_api_ongoing_jo_count:
path: /apiv2/job_orders/ongoing/count
controller: App\Controller\CustomerAppAPI\JobOrderController::getOngoingJobOrderCount
methods: [GET]
cust_api_new_location:
path: /apiv2/new_location
controller: App\Controller\CustomerAppAPI\LocationController::addLocation
methods: [POST]
cust_api_locations:
path: /apiv2/locations
controller: App\Controller\CustomerAppAPI\LocationController::getLocations
methods: [GET]
cust_api_cust_vehicle_remove:
path: /apiv2/vehicles/{id}/remove
controller: App\Controller\CustomerAppAPI\VehicleController::removeVehicle
methods: [POST]
cust_api_latest_job_order:
path: /apiv2/job_order/latest
controller: App\Controller\CustomerAppAPI\JobOrderController::getLatestJobOrder
methods: [GET]
cust_api_customer_hash_get:
path: /apiv2/customer_hash
controller: App\Controller\CustomerAppAPI\CustomerController::getCustomerHash
methods: [GET]
#cust_api_completed_job_orders:
# path: /apiv2/job_orders/completed
# controller: App\Controller\CustomerAppAPI\JobOrderController::getCompletedJobOrders
# methods: [GET]

View file

@ -0,0 +1,35 @@
customer_location_list:
path: /customer-locations
controller: App\Controller\CustomerLocationController::index
methods: [GET]
customer_location_rows:
path: /customer-locations/rowdata
controller: App\Controller\CustomerLocationController::datatableRows
methods: [POST]
customer_location_add_form:
path: /customer-locations/newform
controller: App\Controller\CustomerLocationController::addForm
methods: [GET]
customer_location_add_submit:
path: /customer-locations
controller: App\Controller\CustomerLocationController::addSubmit
methods: [POST]
customer_location_update_form:
path: /customer-locations/{id}
controller: App\Controller\CustomerLocationController::updateForm
methods: [GET]
customer_location_update_submit:
path: /customer-locations/{id}
controller: App\Controller\CustomerLocationController::updateSubmit
methods: [POST]
customer_location_delete:
path: /customer-locations/{id}
controller: App\Controller\CustomerLocationController::deleteSubmit
methods: [DELETE]

View file

@ -0,0 +1,6 @@
# insurance
insurance_listener:
path: /insurance/listen
controller: App\Controller\InsuranceController::listen
methods: [POST]

View file

@ -0,0 +1,14 @@
item_pricing:
path: /item-pricing
controller: App\Controller\ItemPricingController::index
methods: [GET]
item_pricing_update:
path: /item-pricing
controller: App\Controller\ItemPricingController::formSubmit
methods: [POST]
item_pricing_prices:
path: /item-pricing/{pt_id}/{it_id}/prices
controller: App\Controller\ItemPricingController::itemPrices
methods: [GET]

View file

@ -0,0 +1,34 @@
item_type_list:
path: /item-types
controller: App\Controller\ItemTypeController::index
methods: [GET]
item_type_rows:
path: /item-types/rowdata
controller: App\Controller\ItemTypeController::datatableRows
methods: [POST]
item_type_add_form:
path: /item-types/newform
controller: App\Controller\ItemTypeController::addForm
methods: [GET]
item_type_add_submit:
path: /item-types
controller: App\Controller\ItemTypeController::addSubmit
methods: [POST]
item_type_update_form:
path: /item-types/{id}
controller: App\Controller\ItemTypeController::updateForm
methods: [GET]
item_type_update_submit:
path: /item-types/{id}
controller: App\Controller\ItemTypeController::updateSubmit
methods: [POST]
item_type_delete:
path: /item-types/{id}
controller: App\Controller\ItemTypeController::deleteSubmit
methods: [DELETE]

View file

@ -267,3 +267,8 @@ jo_geofence:
path: /ajax/job-order/geofence
controller: App\Controller\JobOrderController::checkGeofence
methods: [GET]
jo_all_view_form:
path: /job-order/all/view/{id}
controller: App\Controller\JobOrderController::allViewForm
methods: [GET]

View file

@ -0,0 +1,38 @@
motolite_event_list:
path: /motolite_events
controller: App\Controller\MotoliteEventController::index
motolite_event_rows:
path: /motolite_events/rows
controller: App\Controller\MotoliteEventController::rows
methods: [POST]
motolite_event_create:
path: /motolite_events/create
controller: App\Controller\MotoliteEventController::addForm
methods: [GET]
motolite_event_create_submit:
path: /motolite_events/create
controller: App\Controller\MotoliteEventController::addSubmit
methods: [POST]
motolite_event_upload_image:
path: /motolite_events/upload
controller: App\Controller\MotoliteEventController::uploadImage
methods: [POST]
motolite_event_update:
path: /motolite_events/{id}
controller: App\Controller\MotoliteEventController::updateForm
methods: [GET]
motolite_event_update_submit:
path: /motolite_events/{id}
controller: App\Controller\MotoliteEventController::updateSubmit
methods: [POST]
motolite_event_delete:
path: /motolite_events/{id}
controller: App\Controller\MotoliteEventController::destroy
methods: [DELETE]

View file

@ -0,0 +1,16 @@
# paymongo
paymongo_listener:
path: /paymongo/listen
controller: App\Controller\PayMongoController::listen
methods: [POST]
paymongo_payment_success:
path: /paymongo/success
controller: App\Controller\PayMongoController::paymentSuccess
methods: [GET]
paymongo_payment_cancelled:
path: /paymongo/cancelled
controller: App\Controller\PayMongoController::paymentCancelled
methods: [GET]

View file

@ -0,0 +1,34 @@
price_tier_list:
path: /price-tiers
controller: App\Controller\PriceTierController::index
methods: [GET]
price_tier_rows:
path: /price-tiers/rows
controller: App\Controller\PriceTierController::datatableRows
methods: [POST]
price_tier_add_form:
path: /price-tiers/newform
controller: App\Controller\PriceTierController::addForm
methods: [GET]
price_tier_add_submit:
path: /price-tiers
controller: App\Controller\PriceTierController::addSubmit
methods: [POST]
price_tier_update_form:
path: /price-tiers/{id}
controller: App\Controller\PriceTierController::updateForm
methods: [GET]
price_tier_update_submit:
path: /price-tiers/{id}
controller: App\Controller\PriceTierController::updateSubmit
methods: [POST]
price_tier_delete:
path: /price-tiers/{id}
controller: App\Controller\PriceTierController::deleteSubmit
methods: [DELETE]

View file

@ -7,3 +7,17 @@ jo_resq_proc_rows:
path: /resq-job-order/processing-rows
controller: App\Controller\ResqJobOrderController::datatableRows
methods: [POST]
defaults:
tier: "proc"
jo_resq_all:
path: /resq-job-order/all
controller: App\Controller\ResqJobOrderController::listAll
methods: [GET]
jo_resq_all_rows:
path: /resq-job-orer/all
controller: App\Controller\ResqJobOrderController::datatableRows
methods: [POST]
defaults:
tier: "all"

View file

@ -0,0 +1,33 @@
reviewtag_list:
path: /review_tags
controller: App\Controller\ReviewTagController::index
reviewtag_rows:
path: /review_tags/rows
controller: App\Controller\ReviewTagController::rows
methods: [POST]
reviewtag_create:
path: /review_tags/create
controller: App\Controller\ReviewTagController::addForm
methods: [GET]
reviewtag_create_submit:
path: /review_tags/create
controller: App\Controller\ReviewTagController::addSubmit
methods: [POST]
reviewtag_update:
path: /review_tags/{id}
controller: App\Controller\ReviewTagController::updateForm
methods: [GET]
reviewtag_update_submit:
path: /review_tags/{id}
controller: App\Controller\ReviewTagController::updateSubmit
methods: [POST]
reviewtag_delete:
path: /review_tags/{id}
controller: App\Controller\ReviewTagController::destroy
methods: [DELETE]

View file

@ -0,0 +1,34 @@
service_offering_list:
path: /service-offerings
controller: App\Controller\ServiceOfferingController::index
methods: [GET]
service_offering_rows:
path: /service-offerings/rowdata
controller: App\Controller\ServiceOfferingController::datatableRows
methods: [POST]
service_offering_add_form:
path: /service-offerings/newform
controller: App\Controller\ServiceOfferingController::addForm
methods: [GET]
service_offering_add_submit:
path: /service-offerings
controller: App\Controller\ServiceOfferingController::addSubmit
methods: [POST]
service_offering_update_form:
path: /service-offerings/{id}
controller: App\Controller\ServiceOfferingController::updateForm
methods: [GET]
service_offering_update_submit:
path: /service-offerings/{id}
controller: App\Controller\ServiceOfferingController::updateSubmit
methods: [POST]
service_offering_delete:
path: /service-offerings/{id}
controller: App\Controller\ServiceOfferingController::deleteSubmit
methods: [DELETE]

View file

@ -51,7 +51,7 @@ tapi_vehicle_make_list:
tapi_battery_list:
path: /tapi/vehicles/{vid}/compatible_batteries
controller: App\Controller\TAPI\BatteryController::getCompatibleBatteries
methods: [GET]
methods: [POST]
# promos
tapi_promo_list:

View file

@ -13,6 +13,10 @@ parameters:
cvu_brand_id: "%env(CVU_BRAND_ID)%"
country_code: "%env(COUNTRY_CODE)%"
api_version: "%env(API_VERSION)%"
android_app_version: "%env(ANDROID_APP_VERSION)%"
ios_app_version: "%env(IOS_APP_VERSION)%"
insurance_premiums_banner_url: "%env(INSURANCE_PREMIUMS_BANNER_URL)%"
enabled_hub_filters: "%env(ENABLED_HUB_FILTERS)%"
services:
# default configuration for services in *this* file
@ -43,20 +47,6 @@ services:
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
Catalyst\AuthBundle\Service\ACLGenerator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
$acl_file: "%app_acl_file%"
Catalyst\AuthBundle\Service\ACLVoter:
arguments:
$user_class: "App\\Entity\\User"
tags: ['security.voter']
Catalyst\AuthBundle\Service\UserChecker:
App\Service\FileUploader:
arguments:
$target_dir: '%image_upload_directory%'
@ -81,6 +71,11 @@ services:
$redis_client: "@App\\Service\\RedisClientProvider"
$key: "mqtt_events"
App\Service\MQTTClientApiv2:
arguments:
$redis_client: "@App\\Service\\RedisClientProvider"
$key: "mqtt_events"
App\Service\APNSClient:
arguments:
$redis_client: "@App\\Service\\RedisClientProvider"
@ -115,60 +110,25 @@ services:
arguments:
$callback_url: "%env(WARRANTY_SERIAL_CALLBACK_URL)%"
App\Command\ProcessLatePaymongoTransactionsCommand:
arguments:
$em: "@doctrine.orm.entity_manager"
$paymongo: "@App\\Service\\PayMongoConnector"
$webhook_id: "%env(PAYMONGO_WEBHOOK_ID)%"
# rider tracker service
App\Service\RiderTracker:
arguments:
$redis_client: "@App\\Service\\RedisClientProvider"
Catalyst\APIBundle\Security\APIKeyUserProvider:
arguments:
$em: "@doctrine.orm.entity_manager"
Catalyst\APIBundle\Security\APIKeyAuthenticator:
arguments:
$em: "@doctrine.orm.entity_manager"
Catalyst\APIBundle\Command\UserCreateCommand:
arguments:
$em: "@doctrine.orm.entity_manager"
tags: ['console.command']
Catalyst\APIBundle\Command\TestCommand:
tags: ['console.command']
Catalyst\APIBundle\Command\TestAPICommand:
tags: ['console.command']
Catalyst\APIBundle\Access\Voter:
arguments:
$acl_gen: "@Catalyst\\APIBundle\\Access\\Generator"
$user_class: "Catalyst\\APIBundle\\Entity\\User"
tags: ['security.voter']
Catalyst\APIBundle\Access\Generator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
$acl_file: "%api_acl_file%"
Catalyst\MenuBundle\Menu\Generator:
arguments:
$router: "@router.default"
$cache_dir: "%kernel.cache_dir%"
$config_dir: "%kernel.root_dir%/../config"
Catalyst\MenuBundle\Listener\MenuAnnotationListener:
arguments:
$menu_name: "main_menu"
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
# invoice generator
App\Service\InvoiceGenerator\ResqInvoiceGenerator: ~
# invoice generator interface
App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\ResqInvoiceGenerator"
App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceManager"
# invoice manager
App\Service\InvoiceManager: ~
# job order generator
App\Service\JobOrderHandler\ResqJobOrderHandler:
@ -257,6 +217,30 @@ services:
$sub_key: "%env(MOTIV_KEY)%"
$token: "%env(MOTIV_TOKEN)%"
# insurance connector
App\Service\InsuranceConnector:
arguments:
$base_url: "%env(INSURANCE_BASE_URL)%"
$username: "%env(INSURANCE_USERNAME)%"
$password: "%env(INSURANCE_PASSWORD)%"
# entity listener for gateway transactions
App\EntityListener\GatewayTransactionListener:
arguments:
$em: "@doctrine.orm.entity_manager"
$ic: "@App\\Service\\InsuranceConnector"
tags:
- name: doctrine.orm.entity_listener
event: 'postUpdate'
entity: 'App\Entity\GatewayTransaction'
# paymongo connector
App\Service\PayMongoConnector:
arguments:
$base_url: "%env(PAYMONGO_BASE_URL)%"
$public_key: "%env(PAYMONGO_PUBLIC_KEY)%"
$secret_key: "%env(PAYMONGO_SECRET_KEY)%"
# entity listener for customer vehicle warranty code history
App\EntityListener\CustomerVehicleSerialListener:
arguments:
@ -328,3 +312,47 @@ services:
App\Service\WarrantySerialLoadLogger:
arguments:
$em: "@doctrine.orm.entity_manager"
# FCM sender
App\Service\FCMSender:
arguments:
$server_key: "%env(FCM_SERVER_KEY)%"
$sender_id: "%env(FCM_SENDER_ID)%"
# price tier manager
App\Service\PriceTierManager:
arguments:
$em: "@doctrine.orm.entity_manager"
# hub filters
App\Service\HubFilter\BaseHubFilter:
arguments:
$hub_filter_logger: "@App\\Service\\HubFilterLogger"
$em: "@doctrine.orm.entity_manager"
$rt: "@App\\Service\\RisingTideGateway"
$trans: "@Symfony\\Contracts\\Translation\\TranslatorInterface"
App\Service\HubFilter\Filters\DateAndTimeHubFilter:
public: true
App\Service\HubFilter\Filters\JoTypeHubFilter:
public: true
App\Service\HubFilter\Filters\MaxResultsHubFilter:
public: true
App\Service\HubFilter\Filters\PaymentMethodHubFilter:
public: true
App\Service\HubFilter\Filters\RiderAvailabilityHubFilter:
public: true
App\Service\HubFilter\Filters\InventoryHubFilter:
public: true
arguments:
$im: "@App\\Service\\InventoryManager"
App\Service\HubFilter\Filters\RoundRobinHubFilter:
public: true
arguments:
$hub_distributor: "@App\\Service\\HubDistributor"

BIN
public/assets/images/image.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,74 @@
$(function() {
// export table to csv
$(document).on('click', '[data-export-csv]', async function(e) {
const el = e.target.closest('[data-export-csv]');
const oldLabel = el.innerHTML;
// set loading status
el.disabled = true;
el.innerHTML = 'Exporting...';
const formData = new FormData();
formData.append('datatable[pagination][page]', 1);
formData.append('datatable[pagination][perpage]', 10000000);
// get all rows
const response = await fetch(el.dataset.url, {
method: el.dataset.method,
body: formData,
});
const result = await response.json();
if (response.status === 200) {
// empty set returned
if (parseInt(result.meta.total) === 0) {
swal({
title: 'Whoops',
html: 'No data to export!',
type: 'warning',
});
}
// build csv data
const csvRows = [];
const fieldList = el.dataset.fields.split(',');
csvRows.push(el.dataset.headers);
result.data.forEach((row) => {
const fieldData = [];
fieldList.forEach((field) => {
fieldData.push('"' + row[field] + '"');
});
csvRows.push(fieldData.join(','));
});
const csvData = csvRows.join('\n');
// build the csv file
const csvFile = new Blob([csvData], {
type: 'text/csv',
});
// create a link to the file and download it
const url = window.URL.createObjectURL(csvFile);
const a = document.createElement('a');
a.href = url;
a.download = el.dataset.filename + '.csv';
a.click();
} else {
// something went wrong on the server
swal({
title: 'Whoops',
html: 'An error has occurred while retrieving data.',
type: 'error',
});
}
// remove loading status
el.disabled = false;
el.innerHTML = oldLabel;
});
});

BIN
public/battery/enduro_mobile.jpg Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/battery/excel_mobile.jpg Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/battery/gold_mobile.jpg Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -10,8 +10,8 @@ use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Entity\User as APIUser;
use Catalyst\APIBundle\Entity\Role as APIRole;
use App\Entity\ApiUser as APIUser;
use Catalyst\ApiBundle\Entity\Role as APIRole;
use App\Entity\Rider;

View file

@ -0,0 +1,136 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Doctrine\ORM\EntityManagerInterface;
use App\Ramcar\TransactionStatus;
use App\Entity\GatewayTransaction;
use App\Service\PayMongoConnector;
use DateTime;
class ProcessLatePaymongoTransactionsCommand extends Command
{
protected $em;
protected $paymongo;
protected $webhook_id;
public function __construct(EntityManagerInterface $em, PayMongoConnector $paymongo, $webhook_id)
{
$this->em = $em;
$this->paymongo = $paymongo;
$this->webhook_id = $webhook_id;
parent::__construct();
}
protected function configure()
{
$this->setName('paymongo:checkpending')
->setDescription('Check for any late PayMongo transactions and process if needed.')
->setHelp('Check for any late PayMongo transactions and process if needed.')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Ignore webhook status and process anyway.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$force = $input->getOption('force');
// if we aren't forcing, check webhook status first
if (!$force) {
$output->writeln('Checking webhook status...');
// check if webhook is disabled
$webhook = $this->paymongo->getWebhook($this->webhook_id);
if ($webhook['success'] && $webhook['response']['data']['attributes']['status'] === 'enabled') {
$output->writeln('<info>Webhook is enabled, no need to do anything.</info>');
return 0;
} else {
$output->writeln('<comment>Webhook is disabled! Logging event and attempting to re-enable...</comment>');
// attempt re-enabling of webhook
$result = $this->paymongo->enableWebhook($this->webhook_id);
if ($result['success'] && $result['response']['data']['attributes']['status'] ?? null === 'enabled') {
$output->writeln('<info>Webhook ' . $this->webhook_id . ' re-enabled!</info>');
// log event
$this->paymongo->log('WEBHOOK RE-ENABLED', "[]", json_encode($result['response'], JSON_PRETTY_PRINT), 'webhook');
} else {
$output->writeln('<comment>Webhook ' . $this->webhook_id . ' could not be re-enabled.</comment>');
// log event
$this->paymongo->log('WEBHOOK FAILURE', "[]", json_encode($result['response'], JSON_PRETTY_PRINT), 'webhook');
}
}
}
$output->writeln('Fetching all late pending transactions...');
// set date threshold to 24 hours ago
$date_threshold = (new DateTime())->modify('-24 hours');
$transactions = $this->em->getRepository(GatewayTransaction::class)
->createQueryBuilder('t')
->select('t')
->where('t.status = :status')
->andWhere('t.date_create <= :date_threshold')
->setParameter('status', TransactionStatus::PENDING)
->setParameter('date_threshold', $date_threshold)
->getQuery()
->getResult();
$output->writeln('Found '. count($transactions) . ' rows matching criteria.');
$x = 0;
foreach ($transactions as $trans) {
// check paymongo status
$checkout = $this->paymongo->getCheckout($trans->getExtTransactionId());
if ($checkout['success']) {
// check if we have any payments made
$payments = $checkout['response']['data']['attributes']['payments'] ?? [];
if (!empty($payments)) {
$amount_paid = 0;
// for good measure, we get all successful payments and add them up
foreach ($payments as $payment) {
if ($payment['attributes']['status'] === TransactionStatus::PAID) {
$amount_paid = bcadd($amount_paid, $payment['attributes']['amount']);
}
}
// this transaction is fully paid, so we mark it as paid
if (bccomp($trans->getAmount(), $amount_paid) <= 0) {
$trans->setStatus(TransactionStatus::PAID);
$trans->setDatePay(new DateTime());
$this->em->flush();
$output->writeln('Marked transaction '. $trans->getID() . ' as paid.');
$x++;
} else {
$output->writeln('<comment>Insufficient payment amount (' . $amount_paid . '/' . $trans->getAmount() . ') for this transaction: ' . $trans->getID() . '</comment>');
}
} else {
$output->writeln('<comment>No payments found for transaction: ' . $trans->getID() . '</comment>');
}
} else {
$output->writeln('<comment>Checkout not found: ' . $checkout['error']['message'] . '</comment>');
}
}
$output->writeln('<info>Done! Processed ' . $x . ' rows.</info>');
return 0;
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\EntityManagerInterface;
use App\Service\JobOrderManager;
class SetJobOrderCustNewCommand extends Command
{
protected $em;
protected $jo_manager;
public function __construct(EntityManagerInterface $em, JobOrderManager $jo_manager)
{
$this->em = $em;
$this->jo_manager = $jo_manager;
parent::__construct();
}
protected function configure()
{
$this->setName('joborder:setcustomernew')
->setDescription('Set job order\'s customer new flag for existing job orders.')
->setHelp('Set job order\'s customer new flag for existing job orders');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->em;
// pdo connection
$db = $em->getConnection();
// get all the ids for all job orders
$all_query_sql = 'SELECT id AS jo_id, customer_id AS cust_id FROM job_order ORDER BY id';
$all_query_stmt = $db->prepare($all_query_sql);
$all_query_stmt->execute();
$all_jo_results = $all_query_stmt->fetchAll();
$output->writeln('Processing job orders...');
foreach ($all_jo_results as $jo_row)
{
// for each jo id, get the customer id
$jo_id = $jo_row['jo_id'];
$cust_id = $jo_row['cust_id'];
// check how many JOs have that customer id
$jo_count = $this->jo_manager->getCustomerJobOrderCount($cust_id);
// if one or less, set flag_cust_new to true
if ($jo_count <= 1)
$this->updateCustNew($db, $jo_id);
}
$output->writeln('All done!');
return 0;
}
protected function updateCustNew($db, $jo_id)
{
$update_jo_sql = 'UPDATE job_order SET flag_cust_new = :flag_cust_new WHERE id = :jo_id';
$update_jo_stmt = $db->prepare($update_jo_sql);
$update_jo_stmt->execute([
'flag_cust_new' => true,
'jo_id' => $jo_id
]);
}
}

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,8 @@ use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\EntityManagerInterface;
use App\Service\MQTTClient;
use App\Service\MQTTClientApiv2;
use App\Service\FCMSender;
use App\Entity\JobOrder;
use App\Entity\Rider;
@ -21,11 +23,19 @@ class UpdateUnacceptedJobOrdersCommand extends Command
protected $em;
protected $mclient;
public function __construct(EntityManagerInterface $em, MQTTClient $mclient)
// NOTE: for resq2 app
protected $mclientv2;
protected $fcmclient;
public function __construct(EntityManagerInterface $em, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient)
{
$this->em = $em;
$this->mclient = $mclient;
// NOTE: for resq2 app
$this->mclientv2 = $mclientv2;
$this->fcmclient = $fcmclient;
parent::__construct();
}
@ -41,6 +51,10 @@ class UpdateUnacceptedJobOrdersCommand extends Command
$em = $this->em;
$mclient = $this->mclient;
// NOTE: for resq2 app
$mclientv2 = $this->mclientv2;
$fcmclient = $this->fcmclient;
// TODO: get the timeout limit from .env
$timeout = 3;
$current_status = 'assigned';
@ -107,6 +121,10 @@ class UpdateUnacceptedJobOrdersCommand extends Command
'jo_id' => $id,
];
$mclient->sendEvent($jo, $payload);
// NOTE: for resq2 app
$mclientv2->sendEvent($jo, $payload);
$fcmclient->sendJoEvent($jo, "jo_fcm_title_outlet_assign", "jo_fcm_body_outlet_assign");
}
$rider = $jo->getRider();

View file

@ -49,6 +49,8 @@ use App\Service\HubDistributor;
use App\Service\HubFilterLogger;
use App\Service\HubFilteringGeoChecker;
use App\Service\HashGenerator;
use App\Service\JobOrderManager;
use App\Service\PriceTierManager;
use App\Entity\MobileSession;
use App\Entity\Customer;
@ -867,7 +869,7 @@ class APIController extends Controller implements LoggedController
MapTools $map_tools, InventoryManager $im, MQTTClient $mclient,
RiderAssignmentHandlerInterface $rah, PromoLogger $promo_logger,
HubSelector $hub_select, HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger,
HubFilteringGeoChecker $hub_geofence)
HubFilteringGeoChecker $hub_geofence, JobOrderManager $jo_manager)
{
// check required parameters and api key
$required_params = [
@ -928,7 +930,15 @@ class APIController extends Controller implements LoggedController
->setErrorMessage('No customer information found');
return $res->getReturnResponse();
}
// check if customer has more than one job order already
$flag_cust_new = false;
$cust_jo_count = $jo_manager->getCustomerJobOrderCount($cust->getID());
if ($cust_jo_count <= 1)
$flag_cust_new = true;
$jo->setCustomer($cust);
$jo->setCustNew($flag_cust_new);
// validate service type
$stype = $req->request->get('service_type');
@ -1026,8 +1036,19 @@ class APIController extends Controller implements LoggedController
break;
}
// right now, the app does not include trade-ins but this might change in the future
if (empty($trade_in))
$icrit->addEntry($batt, null, 1);
else
$icrit->addEntry($batt, $trade_in, 1);
// set if taxable
$icrit->setIsTaxable();
// set JO source
// old app doesn't have separate jumpstart
$icrit->setSource(TransactionOrigin::CALL);
// send to invoice generator
$invoice = $ic->generateInvoice($icrit);
$jo->setInvoice($invoice);
@ -1307,6 +1328,13 @@ class APIController extends Controller implements LoggedController
$icrit->addBattery($batt);
*/
// set taxable
$icrit->setIsTaxable(true);
// set JO source
// old app doesn't have separate jumpstart
$icrit->setSource(TransactionOrigin::CALL);
// check trade-in
// only allow motolite, other, none
$trade_in = $req->request->get('trade_in');
@ -1321,6 +1349,10 @@ class APIController extends Controller implements LoggedController
break;
}
// right now, the app does not include trade-ins but this might change in the future
if (empty($trade_in))
$icrit->addEntry($batt, null, 1);
else
$icrit->addEntry($batt, $trade_in, 1);
// send to invoice generator
@ -2710,7 +2742,7 @@ class APIController extends Controller implements LoggedController
MapTools $map_tools, InventoryManager $im, MQTTClient $mclient,
RiderAssignmentHandlerInterface $rah, PromoLogger $promo_logger,
HubSelector $hub_select, HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger,
HubFilteringGeoChecker $hub_geofence)
HubFilteringGeoChecker $hub_geofence, JobOrderManager $jo_manager)
{
// check required parameters and api key
$required_params = [
@ -2835,7 +2867,15 @@ class APIController extends Controller implements LoggedController
// ->setErrorMessage('No customer information found');
// return $res->getReturnResponse();
// }
// check if customer has more than one job order already
$flag_cust_new = false;
$cust_jo_count = $jo_manager->getCustomerJobOrderCount($cust->getID());
if ($cust_jo_count <= 1)
$flag_cust_new = true;
$jo->setCustomer($cust);
$jo->setCustNew($flag_cust_new);
// validate service type
$stype = $req->request->get('service_type');
@ -2865,6 +2905,17 @@ class APIController extends Controller implements LoggedController
$icrit = new InvoiceCriteria();
$icrit->setServiceType($stype);
// set taxable
$icrit->setIsTaxable(true);
// set JO source
// old app doesn't have separate jumpstart
$icrit->setSource(TransactionOrigin::CALL);
// set price tier
$pt_id = $this->pt_manager->getPriceTier($jo->getCoordinates());
$icrit->setPriceTier($pt_id);
// check promo
$promo_id = $req->request->get('promo_id');
if (!empty($promo_id))
@ -2933,6 +2984,10 @@ class APIController extends Controller implements LoggedController
break;
}
// right now, the app does not include trade-ins but this might change in the future
if (empty($trade_in))
$icrit->addEntry($batt, null, 1);
else
$icrit->addEntry($batt, $trade_in, 1);
// send to invoice generator

View file

@ -2,8 +2,8 @@
namespace App\Controller;
use Catalyst\APIBundle\Entity\Role as APIRole;
use Catalyst\APIBundle\Access\Generator as APIACLGenerator;
use Catalyst\ApiBundle\Entity\Role as APIRole;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@ -19,7 +19,7 @@ class APIRoleController extends Controller
{
protected $api_acl_gen;
public function __construct(APIACLGenerator $api_acl_gen)
public function __construct(ACLGenerator $api_acl_gen)
{
$this->api_acl_gen = $api_acl_gen;
}
@ -326,7 +326,7 @@ class APIRoleController extends Controller
protected function padAPIACLHierarchy(&$params)
{
// get acl keys hierarchy
$api_acl_data = $this->api_acl_gen->getACL();
$api_acl_data = $this->api_acl_gen->getACL('api');
$params['api_acl_hierarchy'] = $api_acl_data['hierarchy'];
}

View file

@ -2,8 +2,8 @@
namespace App\Controller;
use Catalyst\APIBundle\Entity\User as APIUser;
use Catalyst\APIBundle\Entity\Role as APIRole;
use App\Entity\ApiUser as APIUser;
use Catalyst\ApiBundle\Entity\Role as APIRole;
use Doctrine\ORM\Query;
use Symfony\Component\HttpFoundation\Request;

View file

@ -6,14 +6,14 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\SAPBattery;
use App\Entity\SAPBatterySize;
use App\Entity\SAPBatteryBrand;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
class BatteryController extends APIController
{

View file

@ -8,8 +8,8 @@ use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\Customer;
use App\Entity\CustomerVehicle;
@ -17,9 +17,9 @@ use App\Entity\Vehicle;
use App\Service\HashGenerator;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
class CustomerController extends APIController
class CustomerController extends ApiController
{
protected $acl_gen;

View file

@ -10,8 +10,8 @@ use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Service\RisingTideGateway;
use App\Service\WarrantyAPILogger;
@ -37,10 +37,10 @@ use App\Ramcar\WarrantySource;
use DateTime;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
// third party API
class CustomerWarrantyController extends APIController
class CustomerWarrantyController extends ApiController
{
protected $acl_gen;

View file

@ -6,14 +6,14 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\Dealer;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
class DealerController extends APIController
class DealerController extends ApiController
{
protected $acl_gen;

View file

@ -0,0 +1,53 @@
<?php
namespace App\Controller\CAPI;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\Hub;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
class HubController extends ApiController
{
protected $acl_gen;
public function __construct(ACLGenerator $acl_gen)
{
$this->acl_gen = $acl_gen;
}
public function getAll(EntityManagerInterface $em)
{
// get all hub data order by name
$this->denyAccessUnlessGranted('hub.list', null, 'No access.');
$results = $em->getRepository(Hub::class)->findBy([], ['name' => 'ASC']);
$hubs = [];
foreach($results as $res)
{
$hub_id = $res->getId();
$hub_name = $res->getName();
$hub_address = $res->getAddress();
$hub_branch_code = $res->getBranchCode();
$hubs[$hub_id] = [
'id' => $hub_id,
'name' => $hub_name,
'address' => $hub_address,
'branch_code' => $hub_branch_code,
];
}
$data = [
'hubs' => $hubs,
];
return new APIResponse(true, 'Hubs loaded.', $data);
}
}

View file

@ -0,0 +1,139 @@
<?php
namespace App\Controller\CAPI;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\JobOrder;
use App\Entity\Warranty;
use App\Ramcar\JOStatus;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
class JobOrderController extends ApiController
{
protected $acl_gen;
public function __construct(ACLGenerator $acl_gen)
{
$this->acl_gen = $acl_gen;
}
public function getJobOrder($id, EntityManagerInterface $em)
{
$this->denyAccessUnlessGranted('joborder.find', null, 'No access.');
$jo = $em->getRepository(JobOrder::class)->find($id);
if ($jo == null)
return new APIResponse(false, 'No job order found with that number.', null, 404);
$data = $this->generateJobOrderData($jo, $em);
return new APIResponse(true, 'Job order found.', $data);
}
protected function generateJobOrderData($jo, EntityManagerInterface $em)
{
// customer vehicle
$cv = $jo->getCustomerVehicle();
// customer information
$customer = $jo->getCustomer();
// hub
$hub_name = '';
$hub = $jo->getHub();
if ($hub != null)
$hub_name = $hub->getName();
// check if JO is fulfilled, if not, we leave date_purchase blank
$date_purchase = '';
$serial = '';
$status = $jo->getStatus();
if ($status == JOStatus::FULFILLED)
{
if ($jo->getDateFulfill() != null)
$date_purchase = $jo->getDateFulfill()->format('M d, Y H:i');
// find warranty to get the serial using plate number
$serial = $this->getSerialFromWarranty($cv->getPlateNumber(), $em);
}
$jo_data = [
'id' => $jo->getID(),
'first_name' => $customer->getFirstName(),
'last_name' => $customer->getLastName(),
'mobile_number' => $customer->getPhoneMobile(),
'email' => $customer->getEmail(),
'plate_number' => $cv->getPlateNumber(),
'date_purchase' => $date_purchase,
'address' => $jo->getDeliveryAddress(),
'hub' => $hub_name,
'serial' => $serial,
];
// invoice items
$items = [];
$jo_items = $jo->getInvoice()->getItems();
$non_battery_item_titles = ['Promo discount', 'Trade-in', 'Service'];
foreach ($jo_items as $item)
{
$item_title = $item->getTitle();
// check if title has Promo discount, Trade-in, or Service
$flag_battery = $this->checkIfBatteryInvoiceItem($item_title, $non_battery_item_titles);
if ($flag_battery == true)
{
$items[] = [
'title' => $item->getTitle(),
];
}
}
$jo_data['items'] = $items;
return $jo_data;
}
protected function checkIfBatteryInvoiceItem($item_title, $non_battery_item_titles)
{
foreach ($non_battery_item_titles as $nb_item_title)
{
$pos_result = stripos($item_title, $nb_item_title);
// if found, invoice item is not a battery item
if ($pos_result !== false)
return false;
}
return true;
}
protected function getSerialFromWarranty($plate_number, EntityManagerInterface $em)
{
// NOTE: Modify the search for the latest warranty. This seems hacky.
// get latest warranty using plate number
$warranty_results = $em->getRepository(Warranty::class)->findBy(
['plate_number' => $plate_number],
['date_create' => 'desc']
);
$serial = '';
if (!empty($warranty_results))
{
// get first entry
$warranty = current($warranty_results);
$serial = $warranty->getSerial();
}
return $serial;
}
}

View file

@ -6,14 +6,14 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\Municipality;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
class MunicipalityController extends APIController
class MunicipalityController extends ApiController
{
protected $acl_gen;

View file

@ -7,15 +7,15 @@ use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\PrivacyPolicy;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
// third party API
class PrivacyPolicyController extends APIController
class PrivacyPolicyController extends ApiController
{
protected $acl_gen;

View file

@ -11,8 +11,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\Rider;
use App\Entity\JOEvent;
@ -22,16 +22,21 @@ use App\Entity\BatteryModel;
use App\Entity\BatterySize;
use App\Entity\RiderAPISession;
use App\Entity\User;
use Catalyst\APIBundle\Entity\User as APIUser;
use App\Entity\ApiUser as APIUser;
use App\Entity\JobOrder;
use App\Entity\SAPBattery;
use App\Entity\WarrantySerial;
use App\Service\RedisClientProvider;
use App\Service\RiderCache;
use App\Service\MQTTClient;
use App\Service\MQTTClientApiv2;
use App\Service\FCMSender;
use App\Service\WarrantyHandler;
use App\Service\JobOrderHandlerInterface;
use App\Service\InvoiceGeneratorInterface;
use App\Service\RisingTideGateway;
use App\Service\RiderTracker;
use App\Service\PriceTierManager;
use App\Ramcar\ServiceType;
use App\Ramcar\TradeInType;
@ -46,7 +51,7 @@ use App\Ramcar\DeliveryStatus;
use DateTime;
// third party API for rider
class RiderAppController extends APIController
class RiderAppController extends ApiController
{
/*
public function register(Request $req, EntityManagerInterface $em, RedisClientProvider $redis)
@ -284,8 +289,9 @@ class RiderAppController extends APIController
// do we have a job order?
// $jo = $rider->getActiveJobOrder();
// NOTE: we do not include job orders that have been cancelled
$jo = $rider->getCurrentJobOrder();
if ($jo == null)
if ($jo == null || $jo->getStatus() == JOStatus::CANCELLED)
{
$data = [
'job_order' => null
@ -380,6 +386,7 @@ class RiderAppController extends APIController
'flag_coolant' => $jo->hasCoolant(),
'has_motolite' => $cv->hasMotoliteBattery(),
'delivery_status' => $jo->getDeliveryStatus(),
'flag_sealant' => $jo->hasSealant(),
]
];
}
@ -406,6 +413,11 @@ class RiderAppController extends APIController
if (!empty($msg))
return new APIResponse(false, $msg);
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// TODO: refactor this into a jo handler class, so we don't have to repeat for control center
// set jo status to in transit
@ -437,7 +449,7 @@ class RiderAppController extends APIController
}
public function cancelJobOrder(Request $req, EntityManagerInterface $em, MQTTClient $mclient)
public function cancelJobOrder(Request $req, EntityManagerInterface $em, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient)
{
$required_params = ['jo_id'];
@ -456,6 +468,11 @@ class RiderAppController extends APIController
// TODO: this is a workaround for requeue, because rider app gets stuck in accept / decline screen
return new APIResponse(true, $msg);
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// requeue it, instead of cancelling it
$jo->requeue();
@ -483,6 +500,10 @@ class RiderAppController extends APIController
];
$mclient->sendEvent($jo, $payload);
// NOTE: for resq2 app
$mclientv2->sendEvent($jo, $payload);
$fcmclient->sendJoEvent($jo, "jo_fcm_title_outlet_assign", "jo_fcm_body_outlet_assign");
$data = [];
return new APIResponse(true, 'Job order requeued.', $data);
}
@ -510,6 +531,11 @@ class RiderAppController extends APIController
// get rider's current job order
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB);
@ -550,6 +576,11 @@ class RiderAppController extends APIController
// get rider's current job order
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB_PRE_JO);
@ -590,6 +621,11 @@ class RiderAppController extends APIController
// get rider's current job order
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB_PRE_JO);
@ -630,6 +666,11 @@ class RiderAppController extends APIController
// get rider's current job order
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_START);
@ -648,7 +689,7 @@ class RiderAppController extends APIController
}
public function arrive(Request $req, EntityManagerInterface $em, MQTTClient $mclient)
public function arrive(Request $req, EntityManagerInterface $em, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient)
{
$required_params = ['jo_id'];
@ -671,6 +712,11 @@ class RiderAppController extends APIController
// set jo status to in progress
$jo->setStatus(JOStatus::IN_PROGRESS);
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE);
@ -698,6 +744,10 @@ class RiderAppController extends APIController
];
$mclient->sendEvent($jo, $payload);
// NOTE: for resq2 app
$mclientv2->sendEvent($jo, $payload);
$fcmclient->sendJoEvent($jo, "jo_fcm_title_driver_arrived", "jo_fcm_body_driver_arrived");
$data = [];
return new APIResponse(true, 'Rider arrived at customer location.', $data);
}
@ -725,6 +775,11 @@ class RiderAppController extends APIController
// get rider's current job order
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB);
@ -748,8 +803,56 @@ class RiderAppController extends APIController
return new APIResponse(true, 'Rider arrive at hub.', $data);
}
public function getBatterySizes(Request $req, EntityManagerInterface $em)
{
// get capi user
$capi_user = $this->getUser();
if ($capi_user == null)
return new APIResponse(false, 'User not found.');
// get rider id from capi user metadata
$rider = $this->getRiderFromCAPI($capi_user, $em);
if ($rider == null)
return new APIResponse(false, 'No rider found.');
// get sizes
$qb = $em->getRepository(BatterySize::class)
->createQueryBuilder('bs');
$sizes = $qb->select('bs.id, bs.name')
->orderBy('bs.name', 'asc')
->getQuery()
->getResult();
// response
return new APIResponse(true, '', [
'sizes' => $sizes,
]);
}
public function getTradeInTypes(Request $req, EntityManagerInterface $em)
{
// get capi user
$capi_user = $this->getUser();
if ($capi_user == null)
return new APIResponse(false, 'User not found.');
// get rider id from capi user metadata
$rider = $this->getRiderFromCAPI($capi_user, $em);
if ($rider == null)
return new APIResponse(false, 'No rider found.');
// get trade-in types
$types = TradeInType::getCollection();
// response
return new APIResponse(true, '', [
'types' => $types,
]);
}
public function payment(Request $req, EntityManagerInterface $em, JobOrderHandlerInterface $jo_handler,
RisingTideGateway $rt, WarrantyHandler $wh, MQTTClient $mclient, TranslatorInterface $translator)
RisingTideGateway $rt, WarrantyHandler $wh, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient, TranslatorInterface $translator)
{
$required_params = ['jo_id'];
@ -767,6 +870,22 @@ class RiderAppController extends APIController
if (!empty($msg))
return new APIResponse(false, $msg);
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// need to check if service type is battery sales
// if so, serial is a required parameter
$serial = $req->request->get('serial', '');
if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW)
{
/*
if (empty($serial))
return new APIResponse(false, 'Missing parameter(s): serial');
*/
}
// set invoice to paid
$jo->getInvoice()->setStatus(InvoiceStatus::PAID);
@ -818,7 +937,6 @@ class RiderAppController extends APIController
// create warranty
if($jo_handler->checkIfNewBattery($jo))
{
$serial = null;
$warranty_class = $jo->getWarrantyClass();
$first_name = $jo->getCustomer()->getFirstName();
$last_name = $jo->getCustomer()->getLastName();
@ -871,6 +989,10 @@ class RiderAppController extends APIController
];
$mclient->sendEvent($jo, $payload);
// NOTE: for resq2 app
$mclientv2->sendEvent($jo, $payload);
$fcmclient->sendJoEvent($jo, "jo_fcm_title_fulfilled", "jo_fcm_body_fulfilled");
$data = [];
return new APIResponse(true, 'Job order paid and fulfilled.', $data);
}
@ -898,6 +1020,11 @@ class RiderAppController extends APIController
// get rider's current job order
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB_POST_JO);
@ -939,6 +1066,11 @@ class RiderAppController extends APIController
// get rider's current job order
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB_POST_JO);
@ -1085,7 +1217,178 @@ class RiderAppController extends APIController
return new APIResponse(true, 'Batteries found.', $data);
}
public function changeService(Request $req, EntityManagerInterface $em, InvoiceGeneratorInterface $ic)
public function getBatteryInfo(Request $req, $serial, EntityManagerInterface $em)
{
if (empty($serial))
{
return new APIResponse(false, 'Missing parameter(s): serial');
}
// get capi user
$capi_user = $this->getUser();
if ($capi_user == null)
return new APIResponse(false, 'User not found.');
// get rider id from capi user metadata
$rider = $this->getRiderFromCAPI($capi_user, $em);
if ($rider == null)
return new APIResponse(false, 'No rider found.');
// find battery given serial/sap_code and flag_active is true
$serial = $em->getRepository(WarrantySerial::class)->find($serial);
if (empty($serial)) {
return new APIResponse(false, 'Warranty serial number not found.');
}
$sap_battery = $em->getRepository(SAPBattery::class)->find($serial->getSKU());
if (empty($sap_battery)) {
return new APIResponse(false, 'No battery info found.');
}
$battery = [
'id' => $sap_battery->getID(),
'brand' => $sap_battery->getBrand()->getName(),
'size' => $sap_battery->getSize()->getName(),
'size_id' => $sap_battery->getSize()->getID(),
'trade_in_type' => TradeInType::MOTOLITE,
'container_size' => $sap_battery->getContainerSize()->getName(),
];
return new APIResponse(true, 'Battery info found.', [
'battery' => $battery,
]);
}
public function updateJobOrder(Request $req, EntityManagerInterface $em, InvoiceGeneratorInterface $ic, PriceTierManager $pt_manager)
{
$items = json_decode(file_get_contents('php://input'), true);
// get job order id
if (!isset($items['jo_id']))
return new APIResponse(false, 'Missing parameter(s): jo_id');
// validate jo_id
$jo_id = $items['jo_id'];
if (empty($jo_id) || $jo_id == null)
return new APIResponse(false, 'Missing parameter(s): jo_id');
// get capi user
$capi_user = $this->getUser();
if ($capi_user == null)
return new APIResponse(false, 'User not found.');
// get rider id from capi user metadata
$rider = $this->getRiderFromCAPI($capi_user, $em);
if ($rider == null)
return new APIResponse(false, 'No rider found.');
// get the job order
$jo = $em->getRepository(JobOrder::class)->find($jo_id);
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// check if we have trade in items
$ti_items = [];
if (isset($items['trade_in_items']))
{
// validate the trade in items first
$ti_items = $items['trade_in_items'];
$msg = $this->validateTradeInItems($em, $ti_items);
if (!empty($msg))
return new APIResponse(false, $msg);
}
// get the service type
if (!isset($items['stype_id']))
return new APIResponse(false, 'Missing parameter(s): stype_id');
// validate service type
$stype_id = $items['stype_id'];
if (!ServiceType::validate($stype_id))
return new APIResponse(false, 'Invalid service type - ' . $stype_id);
// save service type
$jo->setServiceType($stype_id);
// validate promo if any. Promo not required
$promo = null;
if (isset($items['promo_id']))
{
$promo_id = $items['promo_id'];
$promo = $em->getRepository(Promo::class)->find($promo_id);
if ($promo == null)
return new APIResponse(false, 'Invalid promo id - ' . $promo_id);
}
// get other parameters, if any: has motolite battery, has warranty doc, with coolant, payment method, with sealant
if (isset($items['flag_motolite_battery']))
{
// get customer vehicle from jo
$cv = $jo->getCustomerVehicle();
$has_motolite = $items['flag_motolite_battery'];
if ($has_motolite == 'true')
$cv->setHasMotoliteBattery(true);
else
$cv->setHasMotoliteBattery(false);
$em->persist($cv);
}
if (isset($items['flag_warranty_doc']))
{
// TODO: what do we do?
}
if (isset($items['flag_coolant']))
{
$has_coolant = $items['flag_coolant'];
if ($has_coolant == 'true')
$jo->setHasCoolant(true);
else
$jo->setHasCoolant(false);
}
if (isset($items['mode_of_payment']))
{
$payment_method = $items['payment_method'];
if (!ModeOfPayment::validate($payment_method))
$payment_method = ModeOfPayment::CASH;
$jo->setModeOfPayment($payment_method);
}
if (isset($items['flag_sealant']))
{
$has_sealant = $items['flag_sealant'];
if ($has_sealant == 'true')
$jo->setHasSealant(true);
else
$jo->setHasSealant(false);
}
// get capi user
$capi_user = $this->getUser();
if ($capi_user == null)
return new APIResponse(false, 'User not found.');
// get rider id from capi user metadata
$rider = $this->getRiderFromCAPI($capi_user, $em);
if ($rider == null)
return new APIResponse(false, 'No rider found.');
// need to get the existing invoice items using jo id and invoice id
$existing_ii = $this->getInvoiceItems($em, $jo);
$this->generateUpdatedInvoice($em, $ic, $jo, $existing_ii, $ti_items, $promo, $pt_manager);
$data = [];
return new APIResponse(true, 'Job order updated.', $data);
}
public function changeService(Request $req, EntityManagerInterface $em, InvoiceGeneratorInterface $ic, PriceTierManager $pt_manager)
{
// $this->debugRequest($req);
@ -1106,6 +1409,11 @@ class RiderAppController extends APIController
if (!empty($msg))
return new APIResponse(false, $msg);
// check if JO can be modified first
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// check service type
$stype_id = $req->request->get('stype_id');
if (!ServiceType::validate($stype_id))
@ -1145,6 +1453,13 @@ class RiderAppController extends APIController
else
$jo->setHasCoolant(false);
// sealant
$flag_sealant = $req->request->get('flag_sealant', 'false');
if ($flag_sealant == 'true')
$jo->setHasSealant(true);
else
$jo->setHasSealant(false);
// has motolite battery
$cv = $jo->getCustomerVehicle();
$has_motolite = $req->request->get('has_motolite', 'false');
@ -1187,6 +1502,11 @@ class RiderAppController extends APIController
$crit->setServiceType($stype_id);
$crit->setCustomerVehicle($cv);
$crit->setHasCoolant($jo->hasCoolant());
$crit->setIsTaxable();
// set price tier
$pt_id = $pt_manager->getPriceTier($jo->getCoordinates());
$crit->setPriceTier($pt_id);
if ($promo != null)
$crit->addPromo($promo);
@ -1226,6 +1546,168 @@ class RiderAppController extends APIController
return new APIResponse(true, 'Job order service changed.', $data);
}
protected function generateUpdatedInvoice(EntityManagerInterface $em, InvoiceGeneratorInterface $ic, JobOrder $jo, $existing_ii, $trade_in_items, $promo, PriceTierManager $pt_manager)
{
// get the service type
$stype = $jo->getServiceType();
// get the source
$source = $jo->getSource();
// get the customer vehicle
$cv = $jo->getCustomerVehicle();
// get coolant if any
$flag_coolant = $jo->hasCoolant();
// get sealant if any
$flag_sealant = $jo->hasSealant();
// check if new promo is null
if ($promo == null)
{
// promo not updated from app so check existing invoice
// get the promo id from existing invoice item
$promo_id = $existing_ii['promo_id'];
if ($promo_id == null)
$promo = null;
else
$promo = $em->getRepository(Promo::class)->find($promo_id);
}
// populate Invoice Criteria
$icrit = new InvoiceCriteria();
$icrit->setServiceType($stype)
->setCustomerVehicle($cv)
->setSource($source)
->setHasCoolant($flag_coolant)
->setHasSealant($flag_sealant)
->setIsTaxable();
// set price tier
$pt_id = $pt_manager->getPriceTier($jo->getCoordinates());
$icrit->setPriceTier($pt_id);
// at this point, all information should be valid
// assuming JO information is already valid since this
// is in the system already
// add promo if any to criteria
if ($promo != null)
$icrit->addPromo($promo);
// get the battery purchased from existing invoice items
// add the batteries ordered to criteria
$ii_items = $existing_ii['invoice_items'];
foreach ($ii_items as $ii_item)
{
$batt_id = $ii_item['batt_id'];
$qty = $ii_item['qty'];
$battery = $em->getRepository(Battery::class)->find($batt_id);
$icrit->addEntry($battery, null, $qty);
}
// add the trade in items to the criteria
foreach ($trade_in_items as $ti_item)
{
$batt_size_id = $ti_item['battery_size_id'];
$qty = $ti_item['qty'];
$trade_in_type = $ti_item['trade_in_type'];
$batt_size = $em->getRepository(BatterySize::class)->find($batt_size_id);
$icrit->addTradeInEntry($batt_size, $trade_in_type, $qty);
}
// call generateInvoice
$invoice = $ic->generateInvoice($icrit);
// remove previous invoice
$old_invoice = $jo->getInvoice();
$em->remove($old_invoice);
$em->flush();
// save new invoice
$jo->setInvoice($invoice);
$em->persist($invoice);
// log event?
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::RIDER_EDIT)
->setJobOrder($jo)
->setRider($jo->getRider());
$em->persist($event);
$em->flush();
}
protected function getInvoiceItems(EntityManagerInterface $em, JobOrder $jo)
{
$jo_id = $jo->getID();
$conn = $em->getConnection();
// need to get the ordered battery id and quantity from invoice item
// and the promo from invoice
$query_sql = 'SELECT ii.battery_id AS battery_id, ii.qty AS qty, i.promo_id AS promo_id
FROM invoice_item ii, invoice i
WHERE ii.invoice_id = i.id
AND i.job_order_id = :jo_id
AND ii.battery_id IS NOT NULL';
$query_stmt = $conn->prepare($query_sql);
$query_stmt->bindValue('jo_id', $jo_id);
$results = $query_stmt->executeQuery();
$promo_id = null;
$invoice_items = [];
while ($row = $results->fetchAssociative())
{
$promo_id = $row['promo_id'];
$invoice_items[] = [
'batt_id' => $row['battery_id'],
'qty' => $row['qty'],
'trade_in' => ''
];
}
$data = [
'promo_id' => $promo_id,
'invoice_items' => $invoice_items
];
return $data;
}
protected function validateTradeInItems(EntityManagerInterface $em, $ti_items)
{
$msg = '';
foreach ($ti_items as $ti_item)
{
$bs_id = $ti_item['battery_size_id'];
$ti_type = $ti_item['trade_in_type'];
// validate the battery size id
$batt_size = $em->getRepository(BatterySize::class)->find($bs_id);
if ($batt_size == null)
{
$msg = 'Invalid battery size for trade in: ' . $bs_id;
return $msg;
}
// validate the trade in type
if (!TradeInType::validate($ti_type))
{
$msg = 'Invalid trade in type: ' . $ti_type;
return $msg;
}
}
return $msg;
}
protected function getCAPIUser($id, EntityManagerInterface $em)
{
$capi_user = $em->getRepository(APIUser::class)->find($id);
@ -1305,6 +1787,42 @@ class RiderAppController extends APIController
return $msg;
}
protected function checkJOProgressionAllowed(EntityManagerInterface $em, JobOrder $jo, &$rider)
{
$allowed = true;
error_log("JO delivery status is " . $jo->getDeliveryStatus() . " (not allowed: " . DeliveryStatus::CANCELLED . ")");
error_log("JO status is " . $jo->getStatus() . " (not allowed: " . JOStatus::CANCELLED . ")");
// TODO: add more statuses to block if needed, hence. this is a failsafe in case MQTT is not working.
// check delivery status
switch ($jo->getDeliveryStatus())
{
case DeliveryStatus::CANCELLED:
$allowed = false;
break;
}
// check JO status as well
switch ($jo->getStatus())
{
case JOStatus::CANCELLED:
$allowed = false;
break;
}
// if this is the rider's current JO, set to null
if (!$allowed) {
if ($rider->getCurrentJobOrder() === $jo) {
$rider->setCurrentJobOrder();
$em->persist($rider);
$em->flush();
}
}
return $allowed;
}
protected function debugRequest(Request $req)
{
$all = $req->request->all();

View file

@ -5,10 +5,10 @@ namespace App\Controller\CAPI;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Response\APIResponse;
class TestController extends APIController
class TestController extends ApiController
{
public function test()
{

View file

@ -6,14 +6,14 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\Vehicle;
use App\Entity\VehicleManufacturer;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
class VehicleController extends APIController
class VehicleController extends ApiController
{
protected $acl_gen;
@ -48,7 +48,7 @@ class VehicleController extends APIController
{
$this->denyAccessUnlessGranted('vehicle.list', null, 'No access.');
$mfg = $this->em->getRepository(VehicleManufacturer::class)->find($mfg_id);
$mfg = $em->getRepository(VehicleManufacturer::class)->find($mfg_id);
// manufacturer not found
if ($mfg == null)
@ -69,6 +69,9 @@ class VehicleController extends APIController
}
// TODO: need to add manufacturer details
$data = [
'vehicles' => $make_data,
];
return new APIResponse(true, 'Vehicles loaded.', $data);
}

View file

@ -7,8 +7,8 @@ use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\Warranty;
use App\Entity\BatteryModel;
@ -33,10 +33,10 @@ use App\Ramcar\WarrantySource;
use DateTime;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
// third party API
class WarrantyController extends APIController
class WarrantyController extends ApiController
{
protected $acl_gen;
@ -330,7 +330,7 @@ class WarrantyController extends APIController
// get the api_user that made the call so that it gets added to the source
// source becomes CAPI_USER_<insert name of api user here>
$username = $this->getAPIUsername($em, $user_id);
$username = $this->getUser()->getName();
$source = 'CAPI_USER_' . $username;

View file

@ -10,8 +10,8 @@ use Doctrine\ORM\Query;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Catalyst\APIBundle\Controller\APIController;
use Catalyst\APIBundle\Response\APIResponse;
use Catalyst\ApiBundle\Controller\ApiController;
use Catalyst\ApiBundle\Component\Response as APIResponse;
use App\Entity\WarrantySerialQueue;
@ -19,10 +19,10 @@ use App\Service\WarrantySerialUploadLogger;
use DateTime;
use Catalyst\APIBundle\Access\Generator as ACLGenerator;
use Catalyst\AuthBundle\Service\ACLGenerator as ACLGenerator;
// third party API
class WarrantySerialController extends APIController
class WarrantySerialController extends ApiController
{
protected $acl_gen;
protected $upload_logger;

View file

@ -0,0 +1,207 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\Translation\TranslatorInterface;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Entity\CustomerDeleteRequest;
use App\Entity\Customer;
use App\Service\RisingTideGateway;
use DateTime;
class AccountController extends ApiController
{
public function deleteAccount(RisingTideGateway $rt, Request $req, TranslatorInterface $translator)
{
// validate params
$missing = $this->hasMissingParams($req, [
'phone_number',
]);
if ($missing) {
return new ApiResponse(false, $missing);
}
// user input
$phone_number = $req->request->get('phone_number');
$reason = $req->request->get('reason');
// use the test code if we're using a test number or are on test mode
$code = $this->getConfirmCode($phone_number);
$success_msg = 'We have sent a confirmation code to the submitted phone number if it is valid.';
// initialize model
$obj = new CustomerDeleteRequest();
// check if a customer record exists for this phone number
$cust_obj = $this->findCustomerByNumber($phone_number);
if (empty($cust_obj)) {
// return a random id anyway if we don't find this customer
return new ApiResponse(true, $success_msg, [
'request_id' => $obj->getID(),
]);
}
// phone number is valid, we continue building the model
$obj->setPhoneNumber($phone_number);
$obj->setReason($reason);
$obj->setConfirmCode($code);
// send sms to number if not in test mode
if ($this->getOtpMode() != 'test') {
$this->sendConfirmationCode($rt, $phone_number, $code, $translator);
}
// save the model
$obj->setDateCodeSent(new DateTime());
$this->em->persist($obj);
$this->em->flush();
// response
return new ApiResponse(true, $success_msg, [
'request_id' => $obj->getID(),
]);
}
public function validateDeleteCode(Request $req)
{
// validate params
$missing = $this->hasMissingParams($req, [
'request_id',
'code',
]);
if ($missing) {
return new ApiResponse(false, $missing);
}
// user input
$code = $req->request->get('code');
$request_id = $req->request->get('request_id');
// get the request
$obj = $this->em->getRepository(CustomerDeleteRequest::class)->findOneBy([
'id' => $request_id,
'confirm_code' => $code,
'flag_confirmed' => false,
'flag_completed' => false,
]);
if (empty($obj)) {
return new ApiResponse(false, 'Your confirmation code is invalid.');
}
// check if a customer record exists for this phone number
$cust_obj = $this->findCustomerByNumber($obj->getPhoneNumber());
if (empty($cust_obj)) {
return new ApiResponse(false, 'No account exists for this phone number.');
}
// confirm the request
$obj->setConfirmed(true);
$obj->setDateConfirmed(new DateTime());
$obj->setCustomer($cust_obj);
$this->em->flush();
// response
return new ApiResponse(true, 'Your request has been submitted for processing.');
}
public function resendCode(Request $req, RisingTideGateway $rt, TranslatorInterface $translator)
{
// validate params
$missing = $this->hasMissingParams($req, [
'request_id',
]);
if ($missing) {
return new ApiResponse(false, $missing);
}
// user input
$request_id = $req->request->get('request_id');
$now = time();
// get the request
$obj = $this->em->getRepository(CustomerDeleteRequest::class)->findOneBy([
'id' => $request_id,
'flag_confirmed' => false,
'flag_completed' => false,
]);
if (empty($obj)) {
return new ApiResponse(false, 'Invalid request details provided.');
}
// prevent resend spamming
if ($now - $obj->getDateCodeSent()->getTimestamp() < 300) {
return new ApiResponse(false, 'You can only request a confirm code every 5 mins.');
}
$success_msg = 'We have re-sent a confirmation code to the submitted phone number if it is valid.';
// check if a customer record exists for this phone number
$cust_obj = $this->findCustomerByNumber($obj->getPhoneNumber());
if (empty($cust_obj)) {
// return successful without resending code if we don't find this customer
return new ApiResponse(true, $success_msg);
}
// use the test code if we're using a test number or are on test mode
$phone_number = $obj->getPhoneNumber();
$code = $this->getConfirmCode($phone_number);
// send sms to number if not in test mode
if ($this->getOtpMode()!= 'test') {
$this->sendConfirmationCode($rt, $phone_number, $code, $translator);
}
// update last sent timestamp
$obj->setDateCodeSent(new DateTime());
$this->em->flush();
// response
return new ApiResponse(true, $success_msg);
}
protected function getConfirmCode($phone_number)
{
// check for hardcoded phone number for app store testing
$test_numbers = explode(",", $_ENV['TEST_PHONE_NUMBERS']);
if (in_array($phone_number, $test_numbers) || $this->getOtpMode() == 'test') {
$code = "123456";
} else {
// generate code
$code = $this->generateConfirmCode();
}
return $code;
}
protected function generateConfirmCode()
{
return sprintf("%06d", mt_rand(100000, 999999));
}
protected function sendConfirmationCode(RisingTideGateway $rt, $phone_number, $code, TranslatorInterface $translator)
{
// send sms to number
$message = $translator->trans('message.confirmation_code') . ' ' . $code;
$rt->sendSMS($phone_number, $translator->trans('message.battery_brand_allcaps'), $message);
}
protected function findCustomerByNumber($number)
{
$cust_obj = $this->em->getRepository(Customer::class)->findOneBy([
'phone_mobile' => $number,
]);
return $cust_obj;
}
protected function getOtpMode()
{
return $_ENV['OTP_MODE'];
}
}

View file

@ -0,0 +1,167 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\HttpKernel\KernelInterface;
use Catalyst\ApiBundle\Controller\ApiController as BaseApiController;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Ramcar\JOStatus;
use App\Entity\Warranty;
use App\Entity\JobOrder;
use App\Entity\CustomerSession;
class ApiController extends BaseApiController
{
protected $em;
protected $session;
public function __construct(EntityManagerInterface $em, KernelInterface $kernel)
{
$this->session = new CustomerSession; // NOTE: original was null
$this->em = $em;
// load env file
$dotenv = new Dotenv();
$dotenv->loadEnv($kernel->getProjectDir() . '/.env');
}
protected function debugRequest(Request $req)
{
$all = $req->request->all();
error_log(print_r($all, true));
}
protected function hasMissingParams(Request $req, $params = [])
{
return $this->checkRequiredParameters($req, $params);
}
protected function validateSession($session_key)
{
// check if the session exists
$session = $this->em->getRepository(CustomerSession::class)->find($session_key);
if ($session === null) {
return false;
}
$this->session = $session;
return true;
}
protected function validateRequest(Request $req, $params = [])
{
$error = $this->hasMissingParams($req, $params);
$session_key = $req->query->get('session_key');
if (!$error) {
if (empty($session_key) || !$this->validateSession($session_key)) {
$error = 'Invalid session key.';
}
}
return [
'is_valid' => !$error,
'error' => $error,
];
}
protected function findWarranty($plate_number)
{
// NOTE: Modify the search for the latest warranty. This seems hacky.
// get latest warranty using plate number
$warranty_results = $this->em->getRepository(Warranty::class)->findBy(
['plate_number' => $plate_number],
['date_create' => 'desc']
);
$warr = [];
// check if warranty_results is empty
if (empty($warranty_results)) {
/*
$res->setError(true)
->setErrorMessage('No warranty found for plate number');
return $res->getReturnResponse();
*/
return $warr;
}
// get first entry
$warranty = current($warranty_results);
// check for null values for battery and date claim and date expire
$batt_model = '';
$batt_size = '';
$sap_batt = '';
$claim_date = '';
$expiry_date = '';
if (!(is_null($warranty->getBatteryModel()))) {
$batt_model = $warranty->getBatteryModel()->getName();
}
if (!(is_null($warranty->getBatterySize()))) {
$batt_size = $warranty->getBatterySize()->getName();
}
if (!(is_null($warranty->getSAPBattery()))) {
$sap_batt = $warranty->getSAPBattery()->getID();
}
if (!(is_null($warranty->getDateClaim()))) {
$claim_date = $warranty->getDateClaim()->format("d M Y");
}
if (!(is_null($warranty->getDateExpire()))) {
$expiry_date = $warranty->getDateExpire()->format("d M Y");
}
$warr[] = [
'id' => $warranty->getID(),
'serial' => $warranty->getSerial(),
'warranty_class' => $warranty->getWarrantyClass(),
'plate_number' => $warranty->getPlateNumber(),
'first_name' => $warranty->getFirstName(),
'last_name' => $warranty->getLastName(),
'mobile_number' => $warranty->getMobileNumber(),
'battery_model' => $batt_model,
'battery_size' => $batt_size,
'sap_battery' => $sap_batt,
'status' => $warranty->getStatus(),
'date_create' => $warranty->getDateCreate()->format("d M Y g:i A"),
'date_purchase' => $warranty->getDatePurchase()->format("d M Y"),
'date_expire' => $expiry_date,
'date_claim' => $claim_date,
'claim_from' => $warranty->getClaimedFrom(),
'is_activated' => $warranty->isActivated() ? 1 : 0,
];
return $warr;
}
protected function getBatteryImageURL($req, $batt)
{
// TODO: workaround for now, we get static image of battery based on model name
$filename = trim(strtolower($batt->getModel()->getName())) . '_mobile.jpg';
$filename = str_replace(" ", "_", $filename);
$file_path = $req->getSchemeAndHttpHost() . $this->generateUrl('static_battery_image') . '/' . $filename;
return $file_path;
}
protected function getOngoingJobOrders($cust)
{
$ongoing_jos = $this->em->getRepository(JobOrder::class)->findBy([
'customer' => $cust,
'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS],
], ['date_schedule' => 'desc']);
return $ongoing_jos;
}
protected function getGeoErrorMessage()
{
return 'Our services are currently limited to some areas in Metro Manila, Baguio, Batangas, Laguna, Cavite, Pampanga, and Palawan. We will update you as soon as we are available in your area. Thank you for understanding. Keep safe!';
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
class AppController extends ApiController
{
public function versionCheck(Request $req)
{
// validate params
$missing = $this->hasMissingParams($req, [
'version',
'os',
]);
if ($missing) {
return new ApiResponse(false, $missing);
}
$need_update = false;
$msg = 'Version is up to date.';
$os = $req->query->get('os');
// putting this in for the future, in case we have diverging versions
//$platform = $req->query->get('platform');
// get only the major version numbers
$app_version = $req->query->get('version');
$app_major = substr($app_version, 0, strripos($app_version, "."));
$latest_version = $this->getParameter($os . '_app_version');
$latest_major = substr($latest_version, 0, strripos($latest_version, "."));
if ($latest_major < $app_major) {
return new ApiResponse(false, 'Invalid application version: ' . $app_version);
}
if ($latest_major > $app_major) {
$need_update = true;
$msg = 'Your version is outdated and needs an update to use the latest features RES-Q has to offer.';
}
// response
return new ApiResponse(true, '', [
'need_update' => $need_update,
'latest_version' => $latest_version,
'message' => $msg,
]);
}
}

View file

@ -0,0 +1,292 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Doctrine\DBAL\DBALException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\Translation\TranslatorInterface;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Entity\Customer;
use App\Entity\CustomerUser;
use App\Entity\CustomerSession;
use App\Service\RisingTideGateway;
use DateTime;
class AuthController extends ApiController
{
public function register(Request $req)
{
// validate params
$missing = $this->hasMissingParams($req, [
'phone_model',
'os_type',
'os_version',
'phone_id',
]);
if ($missing) {
return new ApiResponse(false, $missing);
}
// retry until we get a unique id
while (true) {
try {
// instantiate session
$sess = new CustomerSession();
$sess->setPhoneModel($req->request->get('phone_model'))
->setOSType($req->request->get('os_type'))
->setOSVersion($req->request->get('os_version'))
->setPhoneID($req->request->get('phone_id'));
// reopen in case we get an exception
if (!$this->em->isOpen()) {
$this->em = $this->em->create(
$this->em->getConnection(),
$this->em->getConfiguration()
);
}
// save
$this->em->persist($sess);
$this->em->flush();
} catch (DBALException $e) {
error_log($e->getMessage());
// delay one second and try again
sleep(1);
continue;
}
break;
}
// return data
return new ApiResponse(true, '', [
'session_key' => $sess->getID(),
]);
}
public function confirmNumber(RisingTideGateway $rt, Request $req, TranslatorInterface $translator)
{
// validate request
$validity = $this->validateRequest($req, [
'phone_number'
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// phone number
$phone_number = $req->request->get('phone_number');
// get otp_mode from .env
$otp_mode = $_ENV['OTP_MODE'];
// check for hardcoded phone number for app store testing
$test_numbers = explode(",", $_ENV['TEST_PHONE_NUMBERS']);
if (in_array($phone_number, $test_numbers)) {
$code = '123456';
$this->session->setConfirmCode($code)
->setPhoneNumber($phone_number);
$this->em->flush();
return new ApiResponse();
}
// check if otp_mode is test
if ($otp_mode == 'test') {
$code = '123456';
$this->session->setConfirmCode($code)
->setPhoneNumber($phone_number);
$this->em->flush();
return new ApiResponse();
}
// TODO: spam protection
// TODO: validate phone number
// generate code and save
$code = $this->generateConfirmCode();
$this->session->setConfirmCode($code)
->setPhoneNumber($phone_number);
$this->em->flush();
if ($otp_mode != 'test') {
// send sms to number
$this->sendConfirmationCode($rt, $phone_number, $code, $translator);
}
// response
return new ApiResponse();
}
public function validateCode(Request $req)
{
// validate request
$validity = $this->validateRequest($req, [
'code',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// already confirmed
if ($this->session->isConfirmed()) {
return new ApiResponse(false, 'User is already confirmed.');
}
// code is wrong
$code = $req->request->get('code');
if ($this->session->getConfirmCode() != $code) {
return new ApiResponse(false, 'Wrong confirm code.');
}
// set confirm date
$date = new DateTime();
$this->session->setDateConfirmed($date)
->setConfirmed();
// figure out if we have customer and customer user already
$customer_user = false;
// TODO: check if we have the number registered before and merge
$dupe_sess = $this->findNumberSession($this->session->getPhoneNumber());
if ($dupe_sess != null) {
error_log("Found existing customer session for " . $this->session->getPhoneNumber());
$dupe_cust = $dupe_sess->getCustomer();
$this->session->setCustomer($dupe_cust);
// set customer user if it exists
$customer_user = $dupe_sess->getCustomerUser() ?? $dupe_cust->getCustomerUser();
}
// TODO: check if mobile matches mobile of customer
$customer = $this->findCustomerByNumber($this->session->getPhoneNumber());
if ($customer != null) {
// TODO: if there is a dupe_sess, do we need to check if
// dupe_cust is the same as the customer we found?
$this->session->setCustomer($customer);
}
if (!$customer_user) {
error_log("We don't have a customer user for session " . $this->session->getID());
$customer_user = $this->findCustomerUserByNumber($this->session->getPhoneNumber());
if ($customer_user === null) {
error_log("Creating a new customer user for " . $this->session->getPhoneNumber());
$customer_user = new CustomerUser();
$customer_user->setCustomer($this->session->getCustomer())
->setPhoneNumber($this->session->getPhoneNumber());
// save
$this->em->persist($customer_user);
$this->em->flush();
} else {
error_log("Found existing customer user for " . $this->session->getPhoneNumber());
}
}
error_log("Customer user ID is " . $customer_user->getID());
// set session customer user
$this->session->setCustomerUser($customer_user);
$this->em->flush();
// response
return new ApiResponse(true, '', [
'api_key' => $customer_user->getApiKey(),
'secret_key'=> $customer_user->getSecretKey(),
]);
}
public function resendCode(Request $req, RisingTideGateway $rt, TranslatorInterface $translator)
{
// validate request
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// already confirmed
if ($this->session->isConfirmed()) {
return new ApiResponse(false, 'User is already confirmed.');
}
// have sent code before
if ($this->session->getDateCodeSent() != null) {
return new ApiResponse(false, 'Can only send confirm code every 5 mins.');
}
// TODO: send via sms
$phone_number = $this->session->getPhoneNumber();
$code = $this->session->getConfirmCode();
$this->sendConfirmationCode($rt, $phone_number, $code, $translator);
// response
return new ApiResponse();
}
protected function generateConfirmCode()
{
return sprintf("%06d", mt_rand(100000, 999999));
}
protected function sendConfirmationCode(RisingTideGateway $rt, $phone_number, $code, TranslatorInterface $translator)
{
// send sms to number
$message = $translator->trans('message.confirmation_code') . ' ' . $code;
$rt->sendSMS($phone_number, $translator->trans('message.battery_brand_allcaps'), $message);
}
// TODO: find session customer by phone number
protected function findNumberSession($number)
{
$query = $this->em->getRepository(CustomerSession::class)->createQueryBuilder('s')
->where('s.phone_number = :number')
->andWhere('s.customer is not null')
->andWhere('s.customer_user is not null')
->andWhere('s.confirm_flag = 1')
->setParameter('number', $number)
->setMaxResults(1)
->getQuery();
// we just need one
$res = $query->getOneOrNullResult();
return $res;
}
protected function findCustomerByNumber($number)
{
$customers = $this->em->getRepository(Customer::class)->findBy(['phone_mobile' => $number]);
// find the customer with the most number of cars
$car_count = 0;
$cust = null;
foreach ($customers as $customer) {
$vehicles = $customer->getVehicles();
if (count($vehicles) > $car_count) {
$car_count = count($vehicles);
// "save" customer object
$cust = $customer;
}
}
return $cust;
}
protected function findCustomerUserByNumber($number)
{
return $this->em->getRepository(CustomerUser::class)->findOneBy(['phone_number' => $number]);
}
}

View file

@ -0,0 +1,145 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Ramcar\CustomerSource;
use App\Entity\Customer;
use App\Entity\PrivacyPolicy;
use App\Service\HashGenerator;
class CustomerController extends ApiController
{
public function getInfo(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// if no customer found
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(true, '', [
'first_name' => '',
'last_name' => '',
'priv_third_party' => (bool) false,
'priv_promo' => (bool) false,
]);
}
// send back customer details
return new ApiResponse(true, '', [
'first_name' => $cust->getFirstName(),
'last_name' => $cust->getLastName(),
'priv_third_party' => (bool) $cust->getPrivacyThirdParty(),
'priv_promo' => (bool) $cust->getPrivacyPromo(),
]);
}
public function updateInfo(Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'first_name',
'last_name',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$cust = $this->updateCustomerInfo($req);
$policy_mobile_id = $_ENV['POLICY_MOBILE'];
$mobile_policy = $this->em->getRepository(PrivacyPolicy::class)->find($policy_mobile_id);
// set policy id
if ($mobile_policy != null) {
$cust->setPrivacyPolicyMobile($mobile_policy);
}
$this->em->flush();
// response
return new ApiResponse();
}
public function getStatus(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// set data
$data = [];
if ($this->session->isConfirmed()) {
$data['status'] = 'confirmed';
} else {
$data['status'] = 'unconfirmed';
}
return new ApiResponse(true, '', $data);
}
public function getCustomerHash(Request $req, HashGenerator $hash)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// hash customer id
$hashed_id = $hash->getHash($cust->getID());
// response
return new ApiResponse(true, '', [
'cust_hash' => $hashed_id,
]);
}
protected function updateCustomerInfo(Request $req)
{
// create new customer if it's not there
$cust = $this->session->getCustomer();
if ($cust == null) {
$cust = new Customer();
// set customer source
$cust->setCreateSource(CustomerSource::MOBILE);
$this->em->persist($cust);
$this->session->setCustomer($cust);
}
$cust->setFirstName($req->request->get('first_name'))
->setLastName($req->request->get('last_name'))
->setEmail($req->request->get('email', ''))
->setConfirmed($this->session->isConfirmed());
// if customer user isn't set, set it now
if ($cust->getCustomerUser() == null) {
$cust->setCustomerUser($this->session->getCustomerUser());
}
// update mobile phone of customer
$cust->setPhoneMobile($this->session->getPhoneNumber());
return $cust;
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
class DeviceController extends ApiController
{
public function updateDeviceID(Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'device_id',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$device_id = $req->request->get('device_id');
$this->session->setDevicePushID($device_id);
$this->em->flush();
// response
return new ApiResponse();
}
}

View file

@ -0,0 +1,364 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Service\InsuranceConnector;
use App\Service\PayMongoConnector;
use App\Entity\InsuranceApplication;
use App\Entity\GatewayTransaction;
use App\Entity\CustomerVehicle;
use App\Ramcar\InsuranceApplicationStatus;
use App\Ramcar\InsuranceMVType;
use App\Ramcar\InsuranceClientType;
use App\Ramcar\TransactionStatus;
use App\Ramcar\InsuranceBodyType;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use DateTime;
class InsuranceController extends ApiController
{
protected $client;
public function __construct(EntityManagerInterface $em, KernelInterface $kernel, InsuranceConnector $client)
{
parent::__construct($em, $kernel);
$this->client = $client;
}
public function createApplication(Request $req, PayMongoConnector $paymongo, UrlGeneratorInterface $router)
{
// validate params
$validity = $this->validateRequest($req, [
// internal
'customer_vehicle_id',
// client info
'client_type',
'first_name',
//'middle_name', // not required
'surname',
'corporate_name',
// client contact info
'address_number',
//'address_street', // not required
//'address_building', // not required
'address_barangay',
'address_city',
'address_province',
'zipcode',
'mobile_number',
'email_address',
// car info
'make',
'model',
'series',
'color',
//'plate_number', // NOTE: we get this from the internal cv record instead
'mv_file_number',
'motor_number',
'serial_chasis', // NOTE: this is how it's spelled on their API
'year_model',
'mv_type_id',
'body_type',
//'is_public', // not required, boolean, only show field if mv_type_id in [4, 13]
//'orcr_file', // this is a file
// mv_type_id specific fields
//'vehicle_use_type', // not required, only show field if mv_type_id is not in [4, 13]. accepted values are: 'commercial', 'private'
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// conditionally require is_public or vehicle_use_type
switch ($req->request->get('mv_type_id')) {
case 4:
case 13:
if (empty($req->request->get('is_public'))) {
return new ApiResponse(false, 'Missing required parameter(s): is_public is required when mv_type_id is in [4, 13]');
}
break;
default:
if (empty($req->request->get('vehicle_use_type'))) {
return new ApiResponse(false, 'Missing required parameter(s): vehicle_use_type is required when mv_type_id is not in [4, 13]');
}
break;
}
// require the orcr file
if ($req->files->get('orcr_file') === null) {
return new ApiResponse(false, 'Missing required file: orcr_file');
}
// get our listener url
$notif_url = $router->generate('insurance_listener', [], UrlGeneratorInterface::ABSOLUTE_URL);
// get customer and cv info
$cust = $this->session->getCustomer();
$cv = $this->em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle_id'));
if ($cv == null) {
return new ApiResponse(false, 'Invalid customer vehicle id.');
}
// confirm that customer vehicle belongs to customer
if ($cv->getCustomer()->getID() != $cust->getID()) {
return new ApiResponse(false, 'Vehicle does not belong to customer.');
}
// process all our inputs first
$input = $req->request->all();
if (!isset($input['is_public'])) {
$input['is_public'] = false;
}
$input['line'] = $this->getLineType($input['mv_type_id'], $input['vehicle_use_type'], $input['is_public']);
// submit insurance application
$result = $this->client->createApplication(
$cv,
$notif_url,
$input,
$req->files->get('orcr_file')
);
if (!$result['success']) {
return new ApiResponse(false, $result['error']['message']);
}
$premium_amount_int = (int)bcmul($result['response']['premium'], 100);
// build checkout item and metadata
$items = [
[
'name' => "Insurance Premium",
'description' => "Premium fee for vehicle insurance",
'quantity' => 1,
'amount' => $premium_amount_int,
'currency' => 'PHP',
],
];
$now = new DateTime();
// create gateway transaction
$gt = new GatewayTransaction();
$gt->setCustomer($cust);
$gt->setDateCreate($now);
$gt->setAmount($premium_amount_int);
$gt->setStatus(TransactionStatus::PENDING);
$gt->setGateway('paymongo'); // TODO: define values elsewhere
$gt->setType('insurance_premium'); // TODO: define values elsewhere
$this->em->persist($gt);
$this->em->flush();
// create paymongo checkout resource
$checkout = $paymongo->createCheckout(
$cust,
$items,
$gt->getID(),
"Motolite RES-Q Vehicle Insurance",
$router->generate('paymongo_payment_success', [], UrlGeneratorInterface::ABSOLUTE_URL),
$router->generate('paymongo_payment_cancelled', [], UrlGeneratorInterface::ABSOLUTE_URL),
['transaction_id' => $gt->getID()], // NOTE: passing this here too for payment resource metadata
);
if (!$checkout['success']) {
return new ApiResponse(false, $checkout['error']['message']);
}
$checkout_url = $checkout['response']['data']['attributes']['checkout_url'];
// add checkout url and id to transaction metadata
$gt->setExtTransactionId($checkout['response']['data']['id']);
$gt->setMetadata([
'checkout_url' => $checkout_url,
]);
// store application in db
$app = new InsuranceApplication();
$app->setDateSubmit($now);
$app->setCustomer($cust);
$app->setCustomerVehicle($cv);
$app->setGatewayTransaction($gt);
$app->setStatus(InsuranceApplicationStatus::CREATED);
$app->setExtTransactionId($result['response']['id']);
$app->setMetadata($input);
$this->em->persist($app);
// save everything
$this->em->flush();
// return
return new ApiResponse(true, '', [
'app_id' => $app->getID(),
'checkout_url' => $checkout_url,
'premium_amount' => (string)$result['response']['premium'],
]);
}
public function getVehicleMakers(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get maker list
$result = $this->client->getVehicleMakers();
if (!$result['success']) {
return new ApiResponse(false, $result['error']['message']);
}
return new ApiResponse(true, '', [
'makers' => $result['response']['data']['vehicleMakers'],
]);
}
public function getVehicleModels($maker_id, Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get maker list
$result = $this->client->getVehicleModels($maker_id);
if (!$result['success']) {
return new ApiResponse(false, $result['error']['message']);
}
return new ApiResponse(true, '', [
'models' => $result['response']['data']['vehicleModels'],
]);
}
public function getVehicleTrims($model_id, Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get maker list
$result = $this->client->getVehicleTrims($model_id);
if (!$result['success']) {
return new ApiResponse(false, $result['error']['message']);
}
return new ApiResponse(true, '', [
'trims' => $result['response']['data']['vehicleTrims'],
]);
}
public function getMVTypes(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
return new ApiResponse(true, '', [
'mv_types' => InsuranceMVType::getCollection(),
]);
}
public function getClientTypes(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
return new ApiResponse(true, '', [
'mv_types' => InsuranceClientType::getCollection(),
]);
}
public function getPremiumsBanner(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
return new ApiResponse(true, '', [
'url' => $this->getParameter('insurance_premiums_banner_url'),
]);
}
public function getBodyTypes(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$bt_collection = InsuranceBodyType::getCollection();
$body_types = [];
// NOTE: formatting it this way to match how insurance third party API returns their own stuff, so it's all handled one way on the app
foreach ($bt_collection as $bt_key => $bt_name) {
$body_types[] = [
'id' => $bt_key,
'name' => $bt_name,
];
}
return new ApiResponse(true, '', [
'body_types' => $body_types,
]);
}
protected function getLineType($mv_type_id, $vehicle_use_type, $is_public = false)
{
$line = '';
// NOTE: this is a bit of a hack since we're hardcoding values, but this is fine for now
switch ($mv_type_id) {
case '3':
$line = 'mcoc';
break;
case '4':
case '13':
if ($is_public) {
$line = 'lcoc';
} else {
$line = 'mcoc';
}
break;
default:
if ($vehicle_use_type === 'commercial') {
$line = 'ccoc';
} else {
$line = 'pcoc';
}
break;
}
return $line;
}
}

View file

@ -0,0 +1,205 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
use App\Service\InvoiceGeneratorInterface;
use App\Service\PriceTierManager;
use App\Ramcar\InvoiceCriteria;
use App\Ramcar\TradeInType;
use App\Ramcar\TransactionOrigin;
use App\Entity\CustomerVehicle;
use App\Entity\Promo;
use App\Entity\Battery;
use App\Entity\BatterySize;
use App\Entity\Customer;
use App\Entity\CustomerMetadata;
class InvoiceController extends ApiController
{
public function getEstimate(Request $req, InvoiceGeneratorInterface $ic, PriceTierManager $pt_manager)
{
// $this->debugRequest($req);
// validate params
$validity = $this->validateRequest($req, [
'service_type',
'cv_id',
// 'batt_id',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// get customer location from customer_metadata using customer id
$lng = $req->request->get('longitude');
$lat = $req->request->get('latitude');
if ((empty($lng)) || (empty($lat)))
{
// use customer metadata location as basis
$coordinates = $this->getCustomerMetadata($cust);
}
else
$coordinates = new Point($lng, $lat);
// make invoice criteria
$icrit = new InvoiceCriteria();
$icrit->setServiceType($req->request->get('service_type'));
// check promo
$promo_id = $req->request->get('promo_id');
if (!empty($promo_id)) {
$promo = $this->em->getRepository(Promo::class)->find($promo_id);
if ($promo == null) {
return new ApiResponse(false, 'Invalid promo id.');
}
// put in criteria
$icrit->addPromo($promo);
}
// check customer vehicle
$cv = $this->em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id'));
if ($cv == null) {
return new ApiResponse(false, 'Invalid customer vehicle id.');
}
$icrit->setCustomerVehicle($cv);
// check if customer owns vehicle
if ($cust->getID() != $cv->getCustomer()->getID()) {
return new ApiResponse(false, 'Customer does not own vehicle.');
}
// check battery
$batt_id = $req->request->get('batt_id');
if ($batt_id != null) {
$batt = $this->em->getRepository(Battery::class)->find($batt_id);
if ($batt == null) {
return new ApiResponse(false, 'Invalid battery id.');
}
} else
$batt = null;
/*
// put battery in criteria
$icrit->addBattery($batt);
*/
// check trade-in
// only allow motolite, other, none
$trade_in_batt = $req->request->get('trade_in_batt');
$trade_in_type = $req->request->get('trade_in_type');
switch ($trade_in_type) {
case TradeInType::MOTOLITE:
case TradeInType::OTHER:
break;
default:
$trade_in_type = '';
break;
}
// add the actual battery item first
$icrit->addEntry($batt, null, 1);
// DEBUG
// if we have a trade in, add it as well
if (!empty($trade_in_type) && !empty($trade_in_batt)) {
$ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt);
if (!empty($ti_batt_obj)) {
$ti_batt_size_obj = $ti_batt_obj->getSize();
$icrit->addTradeInEntry($ti_batt_size_obj, $trade_in_type, 1);
}
}
// set if taxable
$icrit->setIsTaxable();
// set JO source
$icrit->setSource(TransactionOrigin::MOBILE_APP);
// set price tier
$pt_id = 0;
if ($coordinates != null)
{
error_log('coordinates are not null');
$pt_id = $pt_manager->getPriceTier($coordinates);
}
else
error_log('null?');
$icrit->setPriceTier($pt_id);
// send to invoice generator
$invoice = $ic->generateInvoice($icrit);
// make invoice json data
$data = [
'total_price' => (float) $invoice->getTotalPrice(),
'vat_ex_price' => (float) $invoice->getVATExclusivePrice(),
'vat' => (float) $invoice->getVAT(),
'discount' => (float) $invoice->getDiscount(),
'trade_in' => (float) $invoice->getTradeIn(),
];
$items = $invoice->getItems();
$items_data = [];
foreach ($items as $item) {
$my_data = [
'title' => $item->getTitle(),
'qty' => (int) $item->getQuantity() + 0,
'price' => (float) $item->getPrice() + 0.0,
];
$item_batt = $item->getBattery();
if ($item_batt != null) {
$my_data['image_url'] = $this->getBatteryImageURL($req, $item_batt);
}
$items_data[] = $my_data;
}
$data['items'] = $items_data;
// error_log(print_r($data, true));
// response
return new ApiResponse(true, '', $data);
}
protected function getCustomerMetadata(Customer $cust)
{
$coordinates = null;
// check if customer already has existing metadata
$c_meta = $this->em->getRepository(CustomerMetadata::class)->findOneBy(['customer' => $cust]);
if ($c_meta != null)
{
$meta_data = $c_meta->getAllMetaInfo();
foreach ($meta_data as $m_info)
{
if ((isset($m_info['longitude'])) && (isset($m_info['latitude'])))
{
$lng = $m_info['longitude'];
$lat = $m_info['latitude'];
$coordinates = new Point($lng, $lat);
}
}
}
return $coordinates;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,633 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
use App\Ramcar\JOStatus;
use App\Service\GeofenceTracker;
use App\Service\InventoryManager;
use App\Service\MapTools;
use App\Entity\Hub;
use App\Entity\Battery;
use App\Entity\CustomerMetadata;
use DateTime;
use DateInterval;
class LocationController extends ApiController
{
public function locationSupport(Request $req, GeofenceTracker $geo)
{
// validate params
$validity = $this->validateRequest($req, [
'longitude',
'latitude',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$long = $req->query->get('longitude');
$lat = $req->query->get('latitude');
// NOTE: had to add this for promo tag
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
$is_covered = false;
// check if customer still has promo
if (($cust->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) ||
($cust->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO'))
) {
// if has customer tag, customer has not availed of promo
$is_covered = true;
} else {
// geofence
$is_covered = $geo->isCovered($long, $lat);
}
// geofence
// $is_covered = $geo->isCovered($long, $lat);
$data = [
'longitude' => $long,
'latitude' => $lat,
'supported' => $is_covered,
];
// check if is_covered is false. If so, we need to set the error part in the response
if (!$is_covered) {
return new ApiResponse(false, $this->getGeoErrorMessage(), $data);
}
// response
return new ApiResponse(true, '', $data);
}
public function getNearestHubAndSlots(
Request $req,
MapTools $map_tools
) {
// validate params
$validity = $this->validateRequest($req, [
'longitude',
'latitude',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$coordinates = new Point($req->query->get('longitude'), $req->query->get('latitude'));
// add checking if customer has a pre-registered hub
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// check if customer has customer tag promo
if (($cust->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) ||
($cust->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO'))
) {
// if has customer tag, customer has not availed of promo, get the hub where customer is pre-registered
$car_club_cust_hub = $cust->getCarClubCustomerHub();
if ($car_club_cust_hub != null) {
// need to get the rider slots for the pre-registered hub
$hub = $car_club_cust_hub->getHub();
$nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $map_tools, $hub);
} else {
$nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $map_tools);
if (empty($nearest_hub_slots['hub'])) {
return new ApiResponse(false, 'Thank you for reaching out to us. Please expect a call from us and we will assist you with your request. Thank you and stay safe!');
}
}
} else {
$nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $map_tools);
if (empty($nearest_hub_slots['hub'])) {
return new ApiResponse(false, 'Thank you for reaching out to us. Please expect a call from us and we will assist you with your request. Thank you and stay safe!');
}
}
// response
return new ApiResponse(true, '', [
'hub_id' => $nearest_hub_slots['hub']->getID(),
'hub_slots' => $nearest_hub_slots['slots'],
]);
}
public function addLocation(Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'name',
'address',
'longitude',
'latitude',
'landmark',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// get the information
$name = $req->request->get('name');
$address = $req->request->get('address');
$lng = $req->request->get('longitude');
$lat = $req->request->get('latitude');
$landmark = $req->request->get('landmark');
$loc_info = [
'address' => $address,
'longitude' => $lng,
'latitude' => $lat,
'landmark' => $landmark,
];
// check if customer already has existing metadata
$c_meta = $this->em->getRepository(CustomerMetadata::class)->findOneBy(['customer' => $cust]);
if ($c_meta == null) {
// create new customer meta
$cust_meta = new CustomerMetadata();
$cust_meta->setCustomer($cust);
$cust_meta->addMetaInfo($name, $loc_info);
$this->em->persist($cust_meta);
} else {
// limit locations to 6. If more than 6, pop the first one out
// add location to existing customer meta
$meta_count = count($c_meta->getAllMetaInfo());
if ($meta_count >= 6)
$c_meta->popMetaInfo();
$c_meta->addMetaInfo($name, $loc_info);
}
$this->em->flush();
// response
return new ApiResponse();
}
public function removeLocation($id, Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// find customer metadata and delete entry if present
$cv = $this->em->getRepository(CustomerMetadata::class)->findOneBy(['customer' => $cust]);
if ($cv != null) {
$cv->deleteMetadataInfo(base64_decode($id));
}
$this->em->flush();
// response
return new ApiResponse();
}
public function getLocations(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// get the customer meta for customer
$locations = [];
$cust_meta = $this->em->getRepository(CustomerMetadata::class)->findOneBy(['customer' => $cust]);
if ($cust_meta != null) {
$locations = $cust_meta->getAllMetaInfo();
}
$data = [
'locations' => $locations,
];
// response
return new ApiResponse(true, '', $data);
}
protected function findAdvanceNearestHubAndSlots(Point $coordinates, MapTools $map_tools, $hub = null)
{
$hub_data = [];
if ($hub != null) {
// get the slots of hub
$hub_slots = $this->getHubRiderSlots($hub);
$slots = $hub_slots['slot_data'];
$hub_data = [
'hub' => $hub,
'slots' => $slots,
];
return $hub_data;
}
// get the nearest 10 hubs
$nearest_hubs_with_distance = [];
$hubs = $map_tools->getClosestOpenHubs($coordinates, 10);
foreach ($hubs as $hub) {
$nearest_hubs_with_distance[] = $hub;
// TODO: insert checking for branch code here when inventory manager is up
}
$nearest = null;
$hub_slots = [];
$slot_found = false;
// find the nearest hub
if (!empty($nearest_hubs_with_distance)) {
// get slots of nearest hub right after getting nearest hub.
// then check if hub has available slots. If not, get next nearest hub.
foreach ($nearest_hubs_with_distance as $nhd) {
if (empty($nearest)) {
// get the slots for the hub to check if hub is available for assignment
$hub_slots = $this->getHubRiderSlots($nhd['hub']);
$flag_hub_available = $hub_slots['flag_hub_available'];
if ($flag_hub_available == true) {
$nearest = $nhd;
}
} else {
if ($nhd['distance'] < $nearest['distance']) {
// get the slots for nearest which is nhd right now
$hub_slots = $this->getHubRiderSlots($nhd['hub']);
$flag_hub_available = $hub_slots['flag_hub_available'];
// if hub is available, set hub to nearest
if ($flag_hub_available == true) {
$nearest = $nhd;
}
}
}
}
}
if ($nearest != null) {
// set hub data to what is in nearest
$hub_data = [
'hub' => $nearest['hub'],
'slots' => $hub_slots['slot_data'],
];
}
return $hub_data;
}
protected function getHubRiderSlots(Hub $hub)
{
// check hub's advance orders for the day
/*
// get number of advance orders for the next day if request came in before midnight
// or for current day if request came in after midnight
// check request_time
$request_time = time();
$midnight = strtotime('00:00');
*/
$start_date = new DateTime();
$end_date = new DateTime();
// to keep things simple, just start on next day regardless of midnight timer
$start_date->add(new DateInterval('P1D'));
$end_date->add(new DateInterval('P3D'));
/*
if ($request_time < $midnight)
{
// add +1 to start date to get the next day
// add +3 to date to end date to get the advance orders for the next three days
$start_date->add(new DateInterval('P1D'));
$end_date->add(new DateInterval('P1D'));
}
$end_date->add(new DateInterval('P2D'));
*/
// set time bounds for the start and end date
$start_date->setTime(0, 1);
$end_date->setTime(23, 59);
// NOTE: get advance orders via query
// get JOs assigned to hub that are advance orders and scheduled for the next three days with
// for hub assignment status
$query = $this->em->createQuery('select jo from App\Entity\JobOrder jo where jo.hub = :hub and jo.flag_advance = true and
jo.date_schedule >= :date_start and jo.date_schedule <= :date_end and jo.status != :status_cancelled
and jo.status != :status_fulfilled');
$jos_advance_orders = $query->setParameters([
'hub' => $hub,
'date_start' => $start_date,
'date_end' => $end_date,
'status_cancelled' => JOStatus::CANCELLED,
'status_fulfilled' => JOStatus::FULFILLED,
])
->getResult();
// check request_time
// define slots
$slots = [
'08_09' => '8:00 AM',
'09_10' => '9:00 AM',
'10_11' => '10:00 AM',
'11_12' => '11:00 AM',
'12_13' => '12:00 PM',
'13_14' => '1:00 PM',
'14_15' => '2:00 PM',
'15_16' => '3:00 PM',
'16_17' => '4:00 PM',
];
// get the dates for the next three days
$first_date = $start_date->format('Y-m-d');
$second_date = $start_date->add(new DateInterval('P1D'));
$sec_date = $second_date->format('Y-m-d');
$third_date = $end_date->format('Y-m-d');
// define days
$days = [
$first_date => $first_date,
$sec_date => $sec_date,
$third_date => $third_date,
];
// initialize hub rider slots
$hub_rider_slots = [];
foreach ($days as $day) {
foreach ($slots as $slot_key => $slot) {
$hub_rider_slots[$day][$slot_key] = $hub->getRiderSlots();
}
}
// check each JO's date_schedule, decrement rider_slots if date schedule falls in that slot
foreach ($jos_advance_orders as $jo) {
// get date key
$date_sched = $jo->getDateSchedule();
$date_string = $date_sched->format('Y-m-d');
$hour = $date_sched->format('H');
$slot_id = sprintf('%02d_%02d', $hour, $hour + 1);
// error_log("SLOT - $date_string - $slot_id");
// decrement rider slot
if (isset($hub_rider_slots[$date_string][$slot_id]))
$hub_rider_slots[$date_string][$slot_id]--;
// check if it goes through next slot (10 min allowance)
$mins = $date_sched->format('i');
if ($mins > 10) {
$next_slot_id = sprintf('%02d_%02d', $hour + 1, $hour + 2);
// error_log("NEXT SLOT - $date_string - $next_slot_id");
// decrement rider slot
if (isset($hub_rider_slots[$date_string][$next_slot_id]))
$hub_rider_slots[$date_string][$next_slot_id]--;
}
}
// error_log(print_r($hub_rider_slots, true));
$hub_slots = $this->generateHubSlots($hub_rider_slots, $slots);
// error_log(print_r($hub_slots, true));
return $hub_slots;
}
protected function generateHubSlots($rider_slots, $slots)
{
$data = [];
$total_rslots = 0;
$total_unavailable_rslots = 0;
foreach ($rider_slots as $day_id => $rslot) {
$data[$day_id] = [];
foreach ($rslot as $slot_id => $avail_slots) {
// increment total rider slots
$total_rslots++;
$slot_data = [
'id' => $slot_id,
'label' => $slots[$slot_id],
'available' => true,
];
// mark unavailable ones
if ($avail_slots <= 0) { // increment total number of unavailable slots
$total_unavailable_rslots++;
$slot_data['available'] = false;
}
// add to day data
$data[$day_id][] = $slot_data;
}
}
// check if hub has available slots
$hub_available = true;
// error_log('total rider slots ' . $total_rslots);
// error_log('total unavailable slots ' . $total_unavailable_rslots);
if ($total_rslots == $total_unavailable_rslots) {
// error_log('hub has no available slots');
$hub_available = false;
}
$hs_data = [
'flag_hub_available' => $hub_available,
'slot_data' => $data,
];
return $hs_data;
}
protected function findNearestHub($jo, MapTools $map_tools)
{
// get the nearest 10 hubs
$selected_hub = null;
$hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10, date("H:i:s"));
$nearest_hubs_with_distance = [];
$nearest_branch_codes = [];
foreach ($hubs as $hub) {
$nearest_hubs_with_distance[] = $hub;
//if (!empty($hub['hub']->getBranchCode()))
// $nearest_branch_codes[] = $hub['hub']->getBranchCode();
}
// check if nearest hubs have branch codes
//if (count($nearest_branch_codes) == 0)
// return $selected_hub;
// assume all 10 have stock
// find the nearest hub with available riders
$nearest = null;
foreach ($nearest_hubs_with_distance as $nhd) {
// get number of available riders
$count_riders = count($nhd['hub']->getAvailableRiders());
// get number of advance orders in the next 3 hours
$time_now = new DateTime();
$date_end = new DateTime();
$date_end->add(new DateInterval('PT2H'));
// NOTE: get advance orders via query
// get JOs assigned to hub that are advance orders and scheduled within X hours with
// for rider assignment status
$query = $this->em->createQuery('select count(jo) from App\Entity\JobOrder jo where jo.hub = :hub and jo.flag_advance = true and jo.date_schedule <= :date_end and jo.status = :status');
$count_advance_orders = $query->setParameters([
'hub' => $nhd['hub'],
'date_end' => $date_end,
'status' => JOStatus::RIDER_ASSIGN,
])
->setMaxResults(1)
->getSingleScalarResult();
// error_log('HUB - ' . $nhd['hub']->getID());
// error_log('RIDER COUNT - ' . $count_riders);
// error_log('ADVANCE ORDER COUNT - ' . $count_advance_orders);
// if (count($nhd['hub']->getAvailableRiders()) > 0)
// if we have more riders than we have advance orders
if ($count_riders - $count_advance_orders > 0) {
if (empty($nearest))
$nearest = $nhd;
else {
if ($nhd['distance'] < $nearest['distance'])
$nearest = $nhd;
}
}
}
$selected_hub = $nearest['hub'];
return $selected_hub;
}
protected function findNearestHubWithInventory(
$jo,
Battery $batt,
MapTools $map_tools,
InventoryManager $im
) {
// get the nearest 10 hubs
$selected_hub = null;
$hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10, date("H:i:s"));
$nearest_hubs_with_distance = [];
$nearest_branch_codes = [];
foreach ($hubs as $hub) {
$nearest_hubs_with_distance[] = $hub;
//if (!empty($hub['hub']->getBranchCode()))
// $nearest_branch_codes[] = $hub['hub']->getBranchCode();
}
// check if nearest hubs have branch codes
//if (count($nearest_branch_codes) == 0)
// return $selected_hub;
// assume all 10 have stock
// find the nearest hub with available riders
$nearest = null;
foreach ($nearest_hubs_with_distance as $nhd) {
if (count($nhd['hub']->getAvailableRiders()) > 0) {
if (empty($nearest))
$nearest = $nhd;
else {
if ($nhd['distance'] < $nearest['distance'])
$nearest = $nhd;
}
}
}
$selected_hub = $nearest['hub'];
// uncomment this snippet when inventory check becomes active
// get battery sku
/*
if ($batt != null)
{
$skus[] = $batt->getSAPCode();
// api call to check inventory
// pass the list of branch codes of nearest hubs and the skus
// go through returned list of branch codes
// bypass inventory check for now
// $hubs_with_inventory = $im->getBranchesInventory($nearest_branch_codes, $skus);
if (!empty($hubs_with_inventory))
{
$nearest = [];
$flag_hub_found = false;
foreach ($hubs_with_inventory as $hub_with_inventory)
{
// find hub according to branch code
$found_hub = $this->em->getRepository(Hub::class)->findOneBy(['branch_code' => $hub_with_inventory['BranchCode']]);
if ($found_hub != null)
{
// check rider availability
if (count($found_hub->getAvailableRiders()) > 0)
{
// check against nearest hubs with distance
foreach ($nearest_hubs_with_distance as $nhd)
{
// get distance of hub from location, compare with $nearest. if less, replace nearest
if ($found_hub->getID() == $nhd['hub']->getID())
{
if (empty($nearest))
{
$nearest = $nhd;
$flag_hub_found = true;
}
else
{
if ($nhd['distance'] < $nearest['distance'])
{
$nearest = $nhd;
$flag_hub_found = true;
}
}
}
}
}
}
}
$selected_hub = $nearest['hub'];
}
} */
return $selected_hub;
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Entity\MotoliteEvent;
use App\Ramcar\MotoliteEventType;
class MotoliteEventController extends ApiController
{
public function getEvents(Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'limit',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$limit = $req->query->get('limit');
// get all events
$results = $this->em->getRepository(MotoliteEvent::class)
->findBy([], ['event_time' => 'asc'], $limit);
if (empty($results)) {
return new ApiResponse(false, 'No events available.');
}
$events = [];
foreach ($results as $result) {
$events[] = [
'id' => $result->getID(),
'name' => $result->getName(),
'event_type' => MotoliteEventType::getName($result->getEventType()),
'event_time' => $result->getEventTime()->format("d M Y g:i A"),
'url' => $result->getUrl(),
'image_file' => $result->getImageFile(),
];
}
// response
return new ApiResponse(true, '', [
'events' => $events,
]);
}
}

View file

@ -0,0 +1,164 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Entity\Partner;
use App\Entity\Review;
class PartnerController extends ApiController
{
public function getPartnerInformation(Request $req, $pid)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get partner
$partner = $this->em->getRepository(Partner::class)->findOneBy(['id' => $pid]);
if ($partner == null) {
return new ApiResponse(false, 'No partner found.');
}
// get reviews for partner
$reviews = $this->em->getRepository(Review::class)->findBy(['partner' => $partner]);
// get average rating for all reviews
$average_rating = 0;
if (!empty($reviews)) {
$rating = 0;
foreach ($reviews as $review) {
$rating = $rating + $review->getRating();
}
$average_rating = $rating / sizeof($reviews);
}
$data = [];
$data['partner'] = [
'id' => $partner->getID(),
'name' => $partner->getName(),
'branch' => $partner->getBranch(),
'address' => $partner->getAddress(),
'contact_nums' => $partner->getContactNumbers(),
'time_open' => $partner->getTimeOpen()->format("g:i A"),
'time_close' => $partner->getTimeClose()->format("g:i A"),
'longitude' => $partner->getCoordinates()->getLongitude(),
'latitude' => $partner->getCoordinates()->getLatitude(),
'average_rating' => $average_rating,
];
// response
return new ApiResponse(true, '', $data);
}
public function getClosestPartners(Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'longitude',
'latitude',
'service_id',
'limit',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$long = $req->query->get('longitude');
$lat = $req->query->get('latitude');
$service_id = $req->query->get('service_id');
$limit = $req->query->get('limit');
// get partners within range
$query = $this->em->createQuery('SELECT p, st_distance(p.coordinates, point(:lng, :lat)) as dist FROM App\Entity\Partner p
JOIN App\Entity\Service s where s.id = :service_id ORDER BY dist')
->setParameter('lat', $lat)
->setParameter('lng', $long)
->setParameter('service_id', $service_id);
$query->setMaxResults($limit);
$result = $query->getResult();
$data = [];
$partners = [];
foreach ($result as $row) {
// get all the reviews for each partner and average the ratings
$partner_id = $row[0]->getID();
$partner = $this->em->getRepository(Partner::class)->find($partner_id);
$partner_reviews = $this->em->getRepository(Review::class)->findBy(['partner' => $partner]);
$average_rating = 0;
if (count($partner_reviews) > 0) {
$rating = 0;
foreach ($partner_reviews as $review) {
$rating = $rating + $review->getRating();
}
$average_rating = $rating / sizeof($partner_reviews);
}
$partners[] = [
'id' => $row[0]->getID(),
'name' => $row[0]->getName(),
'branch' => $row[0]->getBranch(),
'address' => $row[0]->getAddress(),
'contact_nums' => $row[0]->getContactNumbers(),
'time_open' => $row[0]->getTimeOpen()->format("g:i A"),
'time_close' => $row[0]->getTimeClose()->format("g:i A"),
'longitude' => $row[0]->getCoordinates()->getLongitude(),
'latitude' => $row[0]->getCoordinates()->getLatitude(),
'db_distance' => $row['dist'],
'rating' => $average_rating,
];
}
$data['partners'] = $partners;
// response
return new ApiResponse(true, '', $data);
}
public function reviewPartner($pid, Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'rating',
'message',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$rating = $req->request->get('rating');
$msg = $req->request->get('message');
// TODO: check rating if 1 - 5
// check if partner exists
$partner = $this->em->getRepository(Partner::class)->find($pid);
if ($partner == null) {
return new ApiResponse(false, 'No partner found.');
}
$rev = new Review();
$rev->setRating($rating)
->setMessage($msg)
->setPartner($partner)
->setCustomerSession($this->session); // NOTE: using new customer session entity
// save to db
$this->em->persist($rev);
$this->em->flush();
// response
return new ApiResponse();
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Entity\PrivacyPolicy;
class PrivacyController extends ApiController
{
public function privacySettings(Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'priv_third_party',
// 'priv_promo',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// set privacy settings
$priv_promo = $req->request->get('priv_promo', false);
$priv_third_party = $req->request->get('priv_third_party');
$cust->setPrivacyThirdParty($priv_third_party)
->setPrivacyPromo($priv_promo);
// get the policy ids from .env
$policy_promo_id = $_ENV['POLICY_PROMO'];
$policy_third_party_id = $_ENV['POLICY_THIRD_PARTY'];
// check if privacy settings are true
// if true, set the private policy for the customer
if ($priv_promo) {
// find the promo policy
$policy = $this->em->getRepository(PrivacyPolicy::class)->find($policy_promo_id);
// set policy id
if ($policy != null) {
$cust->setPrivacyPolicyPromo($policy);
}
}
if ($priv_third_party) {
// find the third party policy
$policy = $this->em->getRepository(PrivacyPolicy::class)->find($policy_third_party_id);
// set policy id
if ($policy != null) {
$cust->setPrivacyPolicyThirdParty($policy);
}
}
$this->em->flush();
// response
return new ApiResponse();
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
class PromoController extends ApiController
{
public function listPromos(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// response
return new ApiResponse();
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Entity\ReviewTag;
class ReviewTagController extends ApiController
{
public function getPartnerReviewTags(Request $req)
{
return $this->getTags($req, "partner");
}
public function getRiderReviewTags(Request $req)
{
return $this->getTags($req, "rider");
}
protected function getTags(Request $req, $tag_type)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get manufacturer list
$tags = $this->em->getRepository(ReviewTag::class)->findBy(['type' => $tag_type], ['name' => 'asc']);
$tag_list = [];
foreach ($tags as $tag) {
$tag_list[] = [
'id' => $tag->getID(),
'name' => $tag->getName(),
];
}
return new ApiResponse(true, '', [
'tags' => $tag_list,
]);
}
}

View file

@ -0,0 +1,241 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Ramcar\JOStatus;
use App\Ramcar\APIRiderStatus;
use App\Entity\RiderRating;
use App\Entity\JobOrder;
use App\Service\RiderTracker;
use Exception;
class RiderController extends ApiController
{
public function getRiderStatus(Request $req, RiderTracker $rt)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// check if we have an ongoing job order
/*
$ongoing_jos = $em->getRepository(JobOrder::class)->findBy([
'customer' => $cust,
'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS],
]);
*/
$ongoing_jos = $this->getOngoingJobOrders($cust);
// $res->setData(['here' => count($ongoing_jos)]);
// return $res->getReturnResponse();
if (count($ongoing_jos) <= 0) {
try {
// check if the latest fulfilled jo they have needs rider rating
$query = $this->em->createQuery('select jo from App\Entity\JobOrder jo where jo.customer = :cust and jo.status = :status order by jo.date_fulfill desc');
$fulfill_jo = $query->setParameters([
'cust' => $cust,
'status' => JOStatus::FULFILLED,
])
->setMaxResults(1)
->getSingleResult();
} catch (Exception $e) {
// response
return new ApiResponse(true, '', [
'status' => APIRiderStatus::NO_PENDING_JO,
]);
}
// we got a recently fulfilled job order
if ($fulfill_jo) {
// check if the rider has been rated
if (!$fulfill_jo->hasRiderRating()) {
$dest = $fulfill_jo->getCoordinates();
$data = [
'jo_id' => $fulfill_jo->getID(),
'service_type' => $fulfill_jo->getServiceType(),
'destination' => [
'long' => $dest->getLongitude(),
'lat' => $dest->getLatitude(),
],
'delivery_address' => $fulfill_jo->getDeliveryAddress(),
'delivery_instructions' => $fulfill_jo->getDeliveryInstructions(),
];
$rider = $fulfill_jo->getRider();
// default image url
$url_prefix = $req->getSchemeAndHttpHost();
$image_url = $url_prefix . '/assets/images/user.gif';
if ($rider->getImageFile() != null)
$image_url = $url_prefix . '/uploads/' . $rider->getImageFile();
$data['status'] = APIRiderStatus::RIDER_RATING;
// default rider location to hub
$data['rider'] = [
'id' => $rider->getID(),
'name' => $rider->getFullName(),
'plate_num' => $rider->getPlateNumber(),
'contact_num' => $rider->getContactNumber(),
'image_url' => $image_url,
];
// response
return new ApiResponse(true, '', $data);
}
}
// response, no pending
return new ApiResponse(true, '', [
'status' => APIRiderStatus::NO_PENDING_JO,
]);
}
// get first jo that's pending
$jo = $ongoing_jos[0];
$dest = $jo->getCoordinates();
$data = [
'jo_id' => $jo->getID(),
'service_type' => $jo->getServiceType(),
'destination' => [
'long' => $dest->getLongitude(),
'lat' => $dest->getLatitude(),
],
'delivery_address' => $jo->getDeliveryAddress(),
'delivery_instructions' => $jo->getDeliveryInstructions(),
];
switch ($jo->getStatus()) {
case JOStatus::PENDING:
$data['status'] = APIRiderStatus::OUTLET_ASSIGN;
break;
case JOStatus::RIDER_ASSIGN:
$data['status'] = APIRiderStatus::RIDER_ASSIGN;
break;
case JOStatus::ASSIGNED:
case JOStatus::IN_TRANSIT:
case JOStatus::IN_PROGRESS:
$rider = $jo->getRider();
// get rider coordinates from redis
$coord = $rt->getRiderLocation($rider->getID());
// default image url
$url_prefix = $req->getSchemeAndHttpHost();
$image_url = $url_prefix . '/assets/images/user.gif';
if ($rider->getImageFile() != null)
$image_url = $url_prefix . '/uploads/' . $rider->getImageFile();
$data['status'] = APIRiderStatus::RIDER_PICK_UP;
// TODO: fix this to actual location of rider
// default rider location to hub
$data['rider'] = [
'id' => $rider->getID(),
'name' => $rider->getFullName(),
'plate_num' => $rider->getPlateNumber(),
'contact_num' => $rider->getContactNumber(),
'image_url' => $image_url,
'location' => [
'long' => $coord->getLongitude(),
'lat' => $coord->getLatitude()
]
];
break;
}
// response
return new ApiResponse(true, '', $data);
}
public function addRiderRating(Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'jo_id',
'rating',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// get job order
$jo_id = $req->request->get('jo_id');
$jo = $this->em->getRepository(JobOrder::class)->find($jo_id);
if ($jo == null) {
return new ApiResponse(false, 'No job order found.');
}
// get rider
$rider = $jo->getRider();
if ($rider == null) {
return new ApiResponse(false, 'No rider found.');
}
// check that the customer owns the job order
$jo_cust = $jo->getCustomer();
if ($jo_cust->getID() != $cust->getID()) {
return new ApiResponse(false, 'Job order was not initiated by customer.');
}
// TODO: check job order status, if it's complete
// add rider rating
$rating_num = $req->request->get('rating', -1);
// if rating is -1
if ($rating_num == -1) {
$jo->setHasRiderRating();
$this->em->flush();
// response
return new ApiResponse();
}
$rating = new RiderRating();
$rating->setRider($rider)
->setCustomer($cust)
->setJobOrder($jo)
->setRating($rating_num);
// rider rating comment
$comment = $req->request->get('comment');
if (!empty($comment))
$rating->setComment($comment);
// mark jo as rider rated already
$jo->setHasRiderRating();
$this->em->persist($rating);
$this->em->flush();
// TODO: preliminary rating computation on the entity for now
$rider->updateRatingAverage();
$this->em->persist($rider);
$this->em->flush();
// response
return new ApiResponse();
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use DateTime;
class ScheduleController extends ApiController
{
public function scheduleOptionStatus(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$schedule_choice = true;
// remove the time check after ECQ. This will then always return true
// get current time
$current_datetime = new DateTime();
//$current_datetime = DateTime::createFromFormat('Y-m-d H:i', '2020-04-30 17:01');
// get the hour
$hour = $current_datetime->format('G');
// commenting out the time check since we can now book 24/7
// this will get uncommented out if and when ECQ will kick in
//if (($hour < 8) || ($hour > 16))
// $schedule_choice = false;
// add checking if customer has a pre-registered hub
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// check if customer has customer tag promo
if (($cust->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) ||
($cust->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO'))
) {
// if has customer tag, customer has not availed of promo, get the hub where customer is pre-registered
$car_club_hub = $cust->getCarClubCustomerHub();
if ($car_club_hub != null) {
$schedule_choice = false;
}
}
// schedule_choice will always be true aka customer can opt to
// Book Now or Schedule Order EXCEPT if customer has customer tag promo
// or ECQ comes back
// response
return new ApiResponse(true, '', [
'display_schedule_choice' => $schedule_choice,
]);
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Entity\Service;
class ServiceController extends ApiController
{
public function listServices(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// services
$results = $this->em->getRepository(Service::class)->findAll();
if (empty($results)) {
return new ApiResponse(false, 'No services available.');
}
$services = [];
foreach ($results as $result) {
/*
// get partners
$partners = [];
$service_partners = $result->getPartners();
foreach($service_partners as $sp)
{
$partners[] = [
'id' => $sp->getID(),
'name' => $sp->getName(),
'branch' => $sp->getBranch(),
'address' => $sp->getAddress(),
'contact_nums' => $sp->getContactNumbers(),
'time_open' => $sp->getTimeOpen()->format("g:i A"),
'time_close' => $sp->getTimeClose()->format("g:i A"),
];
}
*/
$services[] = [
'id' => $result->getID(),
'name' => $result->getName(),
'icon' => $result->getIcon(),
// 'partners' => $partners,
];
}
// response
return new ApiResponse(true, '', [
'services' => $services,
]);
}
}

View file

@ -0,0 +1,599 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
use App\Entity\CustomerVehicle;
use App\Entity\JobOrder;
use App\Entity\VehicleManufacturer;
use App\Entity\Vehicle;
use App\Entity\ItemType;
use App\Ramcar\JOStatus;
use App\Ramcar\ServiceType;
use App\Ramcar\TradeInType;
use App\Ramcar\InsuranceApplicationStatus;
use App\Service\PayMongoConnector;
use App\Service\PriceTierManager;
use DateTime;
class VehicleController extends ApiController
{
public function listVehicleManufacturers(Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get manufacturer list
$mfgs = $this->em->getRepository(VehicleManufacturer::class)->findBy(['flag_mobile' => true], ['name' => 'asc']);
$mfg_list = [];
foreach ($mfgs as $mfg) {
$mfg_list[] = [
'id' => $mfg->getID(),
'name' => $mfg->getName(),
];
}
return new ApiResponse(true, '', [
'manufacturers' => $mfg_list,
]);
}
public function listVehicleMakes(Request $req, $mfg_id)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get manufacturer
$mfg = $this->em->getRepository(VehicleManufacturer::class)->find($mfg_id);
if ($mfg == null) {
// response
return new ApiResponse(false, 'Invalid vehicle manufacturer id.');
}
// get makes
$vehicles = $this->em->getRepository(Vehicle::class)->findBy(
[
'flag_mobile' => true,
'manufacturer' => $mfg_id,
],
['make' => 'asc']
);
// $vehicles = $mfg->getVehicles();
$vlist = [];
foreach ($vehicles as $v) {
$vlist[] = [
'id' => $v->getID(),
'make' => trim($v->getMake() . ' ' . $v->getModelYearFormatted(false)),
// 'make' => $v->getMake() . ' ' . $v->getModelYearFrom() . '-' . $v->getModelYearTo(),
];
}
// response
return new ApiResponse(true, '', [
'manufacturer' => [
'id' => $mfg->getID(),
'name' => $mfg->getName(),
],
'makes' => $vlist,
]);
}
public function addVehicle(Request $req)
{
// check requirements
$validity = $this->checkVehicleRequirements($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// customer vehicle
$cv = new CustomerVehicle();
// set object
$res = $this->setCustomerVehicleObject($req, $cv);
if (!$res['success']) {
return new ApiResponse(false, $res['error']);
}
// response
return new ApiResponse(true, '', [
'cv_id' => $res['cv_id'],
]);
}
public function getVehicle(Request $req, $id, PayMongoConnector $paymongo)
{
// check requirements
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer vehicle
$cv = $this->em->getRepository(CustomerVehicle::class)->find($id);
// check if it exists
if ($cv == null) {
return new ApiResponse(false, 'Vehicle does not exist.');
}
// check if it's owned by customer
if ($cv->getCustomer()->getID() != $this->session->getCustomer()->getID()) {
return new ApiResponse(false, 'Invalid vehicle.');
}
// response
return new ApiResponse(true, '', [
'vehicle' => $this->generateVehicleInfo($cv, true, $paymongo),
]);
}
public function updateVehicle(Request $req, $id)
{
// check requirements
$validity = $this->checkVehicleRequirements($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer vehicle
$cv = $this->em->getRepository(CustomerVehicle::class)->find($id);
// check if it exists
if ($cv == null) {
return new ApiResponse(false, 'Vehicle does not exist.');
}
// check if it's owned by customer
if ($cv->getCustomer()->getID() != $this->session->getCustomer()->getID()) {
return new ApiResponse(false, 'Invalid vehicle.');
}
// set object
$res = $this->setCustomerVehicleObject($req, $cv);
if (!$res['success']) {
return new ApiResponse(false, $res['error']);
}
// response
return new ApiResponse(true, '', [
'cv_id' => $res['cv_id'],
]);
}
public function getTradeInEstimate(Request $req, $id)
{
// check requirements
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer vehicle
$cv = $this->em->getRepository(CustomerVehicle::class)->find($id);
// check if it exists
if ($cv == null) {
return new ApiResponse(false, 'Vehicle does not exist.');
}
// check if it's owned by customer
if ($cv->getCustomer()->getID() != $this->session->getCustomer()->getID()) {
return new ApiResponse(false, 'Invalid vehicle.');
}
// check trade in value
$result = $this->getTIEstimateByCV($cv);
// response
return new ApiResponse(true, '', [
'trade_in_batt' => $result['trade_in_batt'],
'trade_in_type' => $result['trade_in_type'],
'trade_in_value' => $result['trade_in_value'],
]);
}
public function listVehicles(Request $req, PayMongoConnector $paymongo)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// vehicles
$cv_list = [];
// $cvs = $cust->getVehicles();
// only get the customer's vehicles whose flag_active is true
$cvs = $this->em->getRepository(CustomerVehicle::class)->findBy(['flag_active' => true, 'customer' => $cust]);
foreach ($cvs as $cv) {
$cv_list[] = $this->generateVehicleInfo($cv, true, $paymongo);
}
// response
return new ApiResponse(true, '', [
'vehicles' => $cv_list,
]);
}
public function getCompatibleBatteries(Request $req, $vid, PriceTierManager $pt_manager)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get vehicle
$vehicle = $this->em->getRepository(Vehicle::class)->find($vid);
if ($vehicle == null) {
return new ApiResponse(false, 'Invalid vehicle.');
}
// get location from request
$lng = $req->query->get('longitude', '');
$lat = $req->query->get('latitude', '');
$batts = $vehicle->getActiveBatteries();
$pt_id = 0;
if ((!(empty($lng))) && (!(empty($lat))))
{
// get the price tier
$coordinates = new Point($lng, $lat);
$pt_id = $pt_manager->getPriceTier($coordinates);
}
// batteries
$batt_list = [];
foreach ($batts as $batt) {
// TODO: Add warranty_tnv to battery information
// check if customer location is in a price tier location
if ($pt_id == 0)
$price = $batt->getSellingPrice();
else
{
// get item type for battery
$item_type = $this->em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']);
if ($item_type == null)
$price = $batt->getSellingPrice();
else
{
$item_type_id = $item_type->getID();
$batt_id = $batt->getID();
// find the item price given price tier id and battery id
$price = $pt_manager->getItemPrice($pt_id, $item_type_id, $batt_id);
}
}
$batt_list[] = [
'id' => $batt->getID(),
'mfg_id' => $batt->getManufacturer()->getID(),
'mfg_name' => $batt->getManufacturer()->getName(),
'model_id' => $batt->getModel()->getID(),
'model_name' => $batt->getModel()->getName(),
'size_id' => $batt->getSize()->getID(),
'size_name' => $batt->getSize()->getName(),
'price' => $price,
'wty_private' => $batt->getWarrantyPrivate(),
'wty_commercial' => $batt->getWarrantyCommercial(),
'image_url' => $this->getBatteryImageURL($req, $batt),
];
}
// response
return new ApiResponse(true, '', [
'vehicle' => [
'id' => $vehicle->getID(),
'mfg_id' => $vehicle->getManufacturer()->getID(),
'mfg_name' => $vehicle->getManufacturer()->getName(),
'make' => $vehicle->getMake(),
'model_year_from' => $vehicle->getModelYearFrom(),
'model_year_to' => $vehicle->getModelYearTo(),
],
'batteries' => $batt_list,
]);
}
public function removeVehicle($id, Request $req)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// get customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return new ApiResponse(false, 'No customer information found.');
}
// find customer vehicle
$cv = $this->em->getRepository(CustomerVehicle::class)->find($id);
if ($cv == null) {
return new ApiResponse(false, 'Invalid customer vehicle id.');
}
// confirm that customer vehicle belongs to customer
if ($cv->getCustomer()->getID() != $cust->getID()) {
return new ApiResponse(false, 'Vehicle does not belong to customer.');
}
// we cannot remove a vehicle from customer if customer vehicle has already has JOs for it.
// instead we set the customer vehicle's flag_active to false
$cv->setActive(false);
$this->em->flush();
// response
return new ApiResponse();
}
protected function getTIEstimateByCV($cv)
{
// compute for trade in value
$trade_in_batt = null;
$trade_in_value = 0;
$trade_in_type = TradeInType::OTHER;
$previous_jo_found = false;
// check for last battery replacement JO
$last_jo = $this->em->getRepository(JobOrder::class)->findOneBy([
'service_type' => [
ServiceType::BATTERY_REPLACEMENT_NEW, ServiceType::BATTERY_REPLACEMENT_WARRANTY
],
'status' => JOStatus::FULFILLED,
'cus_vehicle' => $cv,
], ['date_create' => 'desc']);
if (!empty($last_jo)) {
$items = $last_jo->getInvoice()->getItems();
foreach ($items as $item) {
// find the first battery item and get its trade-in value
$item_battery = $item->getBattery();
if (!empty($item_battery)) {
$trade_in_type = TradeInType::MOTOLITE;
$trade_in_batt = $item_battery->getID();
$previous_jo_found = true;
$size = $item_battery->getSize();
$trade_in_value = $size->getTIPriceMotolite();
break;
}
}
}
// TODO: possibly refactor this bit
// if no valid previous JO is found, base the trade-in value on recommended batteries
if (!$previous_jo_found) {
$comp_batteries = $cv->getVehicle()->getBatteries();
// get the lowest trade-in value from the list of batteries
if (!empty($comp_batteries)) {
foreach ($comp_batteries as $battery) {
$size = $battery->getSize();
$size_ti = $size->getTIPriceOther();
// get the lowest value or set if not set yet
if ($size_ti < $trade_in_value || $trade_in_value == 0) {
$trade_in_value = $size_ti;
$trade_in_batt = $battery->getID();
}
}
}
}
return [
'trade_in_batt' => $trade_in_batt,
'trade_in_type' => $trade_in_type,
'trade_in_value' => $trade_in_value,
];
}
protected function generateVehicleInfo(CustomerVehicle $cv, $include_insurance = false, PayMongoConnector $paymongo)
{
$battery_id = null;
if ($cv->getCurrentBattery() != null)
$battery_id = $cv->getCurrentBattery()->getID();
$wty_ex = null;
if ($cv->getWarrantyExpiration() != null)
$wty_ex = $cv->getWarrantyExpiration()->format('Y-m-d');
$warranty = $this->findWarranty($cv->getPlateNumber());
$cv_name = '';
if ($cv->getName() != null)
$cv_name = $cv->getName();
$row = [
'cv_id' => $cv->getID(),
'mfg_id' => $cv->getVehicle()->getManufacturer()->getID(),
'make_id' => $cv->getVehicle()->getID(),
'name' => $cv_name,
'plate_num' => $cv->getPlateNumber(),
'model_year' => $cv->getModelYear(),
'color' => $cv->getColor(),
'condition' => $cv->getStatusCondition(),
'fuel_type' => $cv->getFuelType(),
'wty_code' => $cv->getWarrantyCode(),
'wty_expire' => $wty_ex,
'curr_batt_id' => $battery_id,
'is_motolite' => $cv->hasMotoliteBattery() ? 1 : 0,
'is_active' => $cv->isActive() ? 1 : 0,
'warranty' => $warranty,
];
// get latest insurance row
if ($include_insurance) {
$insurance = null;
$iobj = $cv->getLatestInsuranceApplication();
if (!empty($iobj)) {
$gt = $iobj->getGatewayTransaction();
$date_complete = $iobj->getDateComplete();
$date_expire = $iobj->getDateExpire();
$status = $iobj->getStatus();
error_log("\r\nTHIS IS THE CURRENT STATUS: " . $status . "\r\n");
// handle the very transient state between a payment being made and receiving the paymongo webhook
// TODO: maybe handle this more elegantly. issue is not sure it is a good idea to update the db for this very transient status as the webhook listener also updates this status right away
switch ($status) {
case InsuranceApplicationStatus::CREATED:
// get latest status on this checkout from paymongo
$checkout = $paymongo->getCheckout($gt->getExtTransactionId());
if ($checkout['success']) {
$payment_intent = $checkout['response']['data']['attributes']['payment_intent'] ?? null;
if (!empty($payment_intent)) {
$intent_status = $payment_intent['attributes']['status'] ?? null;
// TODO: define these paymongo payment intent statuses elsewhere
if ($intent_status === 'processing' || $intent_status === 'succeeded') {
$status = InsuranceApplicationStatus::PAID;
}
}
}
break;
default:
break;
}
$insurance = [
'id' => $iobj->getID(),
'ext_transaction_id' => $iobj->getExtTransactionId(),
'status' => $status,
'coc_url' => $iobj->getCOC(),
'checkout_url' => $gt->getMetadata()['checkout_url'],
'transaction_status' => $gt->getStatus(),
'premium_amount' => (string)bcdiv($gt->getAmount(), 100), // NOTE: hard expressing as string so it's consistent
'date_submit' => $iobj->getDateSubmit()->format('Y-m-d H:i:s'),
'date_complete' => $date_complete ? $date_complete->format('Y-m-d H:i:s') : null,
'date_expire' => $date_expire ? $date_expire->format('Y-m-d H:i:s') : null,
];
// get information changelog
$insurance['changelog'] = $iobj->getMetadata()['changes'] ?? [];
}
$row['latest_insurance'] = $insurance;
}
return $row;
}
protected function checkVehicleRequirements(Request $req)
{
// validate params
return $this->validateRequest($req, [
'make_id',
'name',
'plate_num',
'model_year',
'color',
'condition',
'fuel_type',
]);
// TODO: check valid plate number
// TODO: check valid fuel type (gas / diesel)
// TODO: check current battery id
// TODO: check condition (brand new / second-hand)
// TODO: check is_motolite and is_active (1 or 0)
// TODO: check warranty expiration date (YYYYMMDD)
// TODO: check model year coverage if it fits in between
}
protected function setCustomerVehicleObject(Request $req, CustomerVehicle $cv)
{
// check customer
$cust = $this->session->getCustomer();
if ($cust == null) {
return [
'success' => false,
'error' => 'No customer information found.',
];
}
// get vehicle
$vehicle = $this->em->getRepository(Vehicle::class)->find($req->request->get('make_id'));
if ($vehicle == null) {
return [
'success' => false,
'error' => 'Invalid vehicle make id.',
];
}
$cv->setCustomer($cust)
->setVehicle($vehicle)
->setName($req->request->get('name'))
->setPlateNumber($req->request->get('plate_num'))
->setModelYear($req->request->get('model_year'))
->setColor($req->request->get('color'))
->setFuelType($this->normalizeString($req->request->get('fuel_type')))
->setStatusCondition($this->normalizeString($req->request->get('condition')));
// set warranty code and expiration
// TODO: check warranty requirements
if (!empty($req->request->get('wty_code')))
$cv->setWarrantyCode($req->request->get('wty_code'));
if (!empty($req->request->get('wty_expire')))
$cv->setWarrantyExpiration(new DateTime($req->request->get('wty_expire')));
// TODO: get current battery
// is motolite
if ($req->request->get('is_motolite') == 0)
$cv->setHasMotoliteBattery(false);
else
$cv->setHasMotoliteBattery(true);
// is active
if ($req->request->get('is_active') == 0)
$cv->setActive(false);
else
$cv->setActive(true);
// save
$this->em->persist($cv);
$this->em->flush();
// response
return [
'success' => true,
'cv_id' => $cv->getID(),
];
}
protected function normalizeString($string)
{
return trim(strtolower($string));
}
}

View file

@ -0,0 +1,614 @@
<?php
namespace App\Controller\CustomerAppAPI;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Catalyst\ApiBundle\Component\Response as ApiResponse;
use App\Entity\Warranty;
use App\Entity\WarrantySerial;
use App\Entity\SAPBattery;
use App\Entity\CustomerVehicle;
use App\Service\WarrantyRaffleLogger;
use App\Service\WarrantyAPILogger;
use App\Service\RisingTideGateway;
use App\Ramcar\WarrantySource;
use DateTime;
class WarrantyController extends ApiController
{
public function activateWarranty(Request $req)
{
// validate params
$validity = $this->validateRequest($req, [
'plate_number',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
$plate_number = $req->request->get('plate_number');
// find warranty using plate number
$warranty_results = $this->em->getRepository(Warranty::class)->findBy(
['plate_number' => $plate_number],
['date_create' => 'desc']
);
// check if warranty_results is empty
if (empty($warranty_results)) {
return new ApiResponse(false, 'No warranty found for plate number.');
}
// activate all entries
foreach ($warranty_results as $warranty) {
$warranty->setActivated();
}
$this->em->flush();
// response
return new ApiResponse();
}
public function warrantyCheck($serial, Request $req, WarrantyRaffleLogger $raffle_logger)
{
// validate params
$validity = $this->validateRequest($req);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// check if warranty serial is there
$serial = $this->cleanSerial($serial);
$warr_serial = $this->em->getRepository(WarrantySerial::class)->find($serial);
$warr = $this->em->getRepository(Warranty::class)->findOneBy(['serial' => $serial]);
$batt = null;
$is_registered = false;
if ($warr_serial == null) {
return new ApiResponse(false, 'Invalid warranty serial code.');
}
$today = new DateTime();
$user_id = $req->query->get('session_key');
$raffle_data = [
'user_id' => $user_id,
'serial' => $serial,
'warranty_id' => null,
'action' => '',
'bmodel_name' => '',
'bsize_name' => '',
'first_name' => '',
'last_name' => '',
'plate_number' => '',
'contact_num' => '',
'email' => '',
'address' => '',
];
$data_sent = [];
// if we have a warranty entry for the serial already
if ($warr != null) {
$warr_plate = $warr->getPlateNumber();
$is_registered = true;
$is_customer_warranty = false;
// check if the warranty is registered to a car owned by the customer
$cust = $this->session->getCustomer();
$is_customer_warranty = $this->checkCustomerPlateNumber($warr_plate, $cust);
// null mobile number should be blank string instead
if ($warr->getMobileNumber() == null)
$mobile_num = '';
else
$mobile_num = $warr->getMobileNumber();
$can_edit = $is_customer_warranty;
// if customer plate number matches the one registered on the warranty
if ($is_customer_warranty) {
// purchase date of customer
if ($warr->getDatePurchaseCustomer() != null)
$date_purchase_cust = $warr->getDatePurchaseCustomer()->format('Y-m-d');
else
$date_purchase_cust = $today->format('Y-m-d');
// invoice
if ($warr->getFileInvoice() != null)
$invoice_url = $req->getSchemeAndHttpHost() . '/warranty_uploads/' . $warr->getFileInvoice();
else
$invoice_url = '';
// warranty card
if ($warr->getFileWarrantyCard() != null)
$warr_card_url = $req->getSchemeAndHttpHost() . '/warranty_uploads/' . $warr->getFileWarrantyCard();
else
$warr_card_url = '';
$customer = [
'first_name' => $warr->getFirstName() ?? '',
'last_name' => $warr->getLastName() ?? '',
'mobile_number' => $mobile_num,
'plate_number' => $warr_plate,
'email' => $warr->getEmail() ?? '',
'contact_num' => $warr->getContactNumber() ?? '',
'address' => $warr->getCustomerAddress() ?? '',
];
$other_data = [
'odometer' => (int) $warr->getOdometer() ?? 0,
'date_purchase' => $date_purchase_cust,
'invoice' => $invoice_url,
'warr_card' => $warr_card_url,
'dealer_name' => $warr->getDealerName() ?? '',
'dealer_address' => $warr->getDealerAddress() ?? '',
'branch_code' => $warr->getDealerBranchCode() ?? '',
];
// set customer info and action for raffle log
$raffle_data['action'] = 'serial_check_customer';
$raffle_data['first_name'] = $customer['first_name'];
$raffle_data['last_name'] = $customer['last_name'];
$raffle_data['plate_number'] = $customer['plate_number'];
$raffle_data['email'] = $customer['email'];
$raffle_data['contact_num'] = $customer['contact_num'];
$raffle_data['address'] = $customer['address'];
$raffle_data['warranty_id'] = $warr->getID();
} else {
// hide customer information if customer is not the one registered
$customer = [
'first_name' => '',
'last_name' => '',
'mobile_number' => '',
'plate_number' => '',
'email' => '',
'contact_num' => '',
'address' => '',
];
$other_data = [
'odometer' => 0,
'date_purchase' => $today->format('Y-m-d'),
'invoice' => '',
'warr_card' => '',
'dealer_name' => '',
'dealer_address' => '',
'branch_code' => '',
];
// set action for raffle log
$raffle_data['action'] = 'serial_check_not_customer';
}
} else {
$can_edit = true;
$customer = [
'first_name' => '',
'last_name' => '',
'mobile_number' => '',
'plate_number' => '',
'email' => '',
'contact_num' => '',
'address' => '',
];
$other_data = [
'odometer' => 0,
'date_purchase' => $today->format('Y-m-d'),
'invoice' => '',
'warr_card' => '',
'dealer_name' => '',
'dealer_address' => '',
'branch_code' => '',
];
// set action for raffle log
$raffle_data['action'] = 'serial_check_customer';
}
$sku = $warr_serial->getSKU();
$batt = null;
$cat_name = '';
if ($sku != null)
$batt = $this->em->getRepository(SAPBattery::class)->find($sku);
else {
// get the category name of the serial
$cat_name = $warr_serial->getMetaInfo('category_name');
}
// TODO: put this in a config file
$image_url = $req->getSchemeAndHttpHost() . '/battery/generic.png';
if ($batt != null) {
$battery = [
'brand' => $batt->getBrand()->getName(),
'size' => $batt->getSize()->getName(),
'image_url' => $image_url,
];
} else {
$battery = [
'brand' => $cat_name,
'size' => '',
'image_url' => '',
];
}
// set the rest of the raffle log entry
$raffle_data['bmodel_name'] = $battery['brand'];
$raffle_data['bsize_name'] = $battery['size'];
// log the raffle log
$raffle_logger->logRaffleInfo($data_sent, $raffle_data);
// response
return new ApiResponse(true, '', [
'is_valid' => true,
'is_registered' => $is_registered,
'can_edit' => $can_edit,
'customer' => $customer,
'battery' => $battery,
'odometer' => $other_data['odometer'],
'invoice' => $other_data['invoice'],
'warr_card' => $other_data['warr_card'],
'date_purchase' => $other_data['date_purchase'],
'dealer_name' => $other_data['dealer_name'],
'dealer_address' => $other_data['dealer_address'],
'branch_code' => $other_data['branch_code'],
'message' => [
'register_error' => 'Warranty serial code has already been registered.',
'edit_error' => 'Sorry, warranty is registered under another vehicle not in your list of vehicles.',
],
]);
}
public function warrantyRegister(
$serial,
Request $req,
KernelInterface $kernel,
RisingTideGateway $rt,
TranslatorInterface $trans,
WarrantyRaffleLogger $raffle_logger,
WarrantyAPILogger $logger
) {
// validate params
$validity = $this->validateRequest($req, [
'first_name',
'last_name',
'plate_number',
'date_purchase',
]);
if (!$validity['is_valid']) {
return new ApiResponse(false, $validity['error']);
}
// handle file uploads
$invoice = $req->files->get('invoice');
$warr_card = $req->files->get('warr_card');
// normalize serial
$serial = $this->cleanSerial($serial);
// $serial = trim(strtoupper($serial));
// process picture uploads
$upload_dir = $kernel->getProjectDir() . '/public/warranty_uploads';
$inv_filename = $this->handlePictureUpload($invoice, $upload_dir, $serial, 'invoice');
$wcard_filename = $this->handlePictureUpload($warr_card, $upload_dir, $serial, 'wcard');
$user_id = $req->query->get('session_key');
$log_data = [
'plate_number' => $req->request->get('plate_num'),
'first_name' => $req->request->get('first_name'),
'last_name' => $req->request->get('last_name'),
'date_purchase' => $req->request->get('date_purchase'),
];
$action = 'create/update';
$source = WarrantySource::MOBILE;
// update customer information
// $cust = $this->updateCustomerInfo($req, $em);
// update warranty
$res = $this->updateWarranty(
$rt,
$trans,
$req,
$serial,
$inv_filename,
$wcard_filename,
$logger,
$log_data,
$user_id,
$action,
$source,
$raffle_logger
);
if (!$res['success']) {
return new ApiResponse(false, $res['error']);
}
$this->em->flush();
return new ApiResponse();
}
protected function handlePictureUpload($file, $target_dir, $serial, $name)
{
// error_log("handling $name upload");
// no file sent
if ($file == null) {
error_log("handling $name upload - no file");
return null;
}
// create target dir if it doesn't exist
if (!file_exists($target_dir)) {
if (!mkdir($target_dir, 0744, true)) {
error_log('failed to create folder for warranty pictures');
return null;
}
}
// move file
$filename = $name . '.' . $file->getClientOriginalExtension();
$file->move($target_dir . '/' . $serial, $filename);
// error_log("filename - $filename");
// error_log($target_dir . '/' . $serial . '/' . $filename);
return $serial . '/' . $filename;
}
// TODO: put this in a service
protected function cleanSerial($serial)
{
// trim and make everything upper case
$clean_serial = trim(strtoupper($serial));
// remove QR prefix if it exists
// $prefix = substr($clean_serial, 0, 2);
// if ($prefix == 'QR')
// $clean_serial = substr($clean_serial, 2);
return $clean_serial;
}
protected function checkCustomerPlateNumber($plate_number, $cust)
{
// strip spaces and make all caps
$plate_number = preg_replace('/\s+/', '', strtoupper($plate_number));
// if there's no customer linked to session
if ($cust != null) {
// check all the customer vehicles
$cvs = $cust->getVehicles();
foreach ($cvs as $cv) {
$cv_plate = preg_replace('/\s+/', '', strtoupper($cv->getPlateNumber()));
// did we find a match?
if ($cv_plate == $plate_number) {
return true;
}
}
}
return false;
}
protected function updateWarranty($rt, $trans, $req, $serial, $inv_filename = null, $wcard_filename = null, $logger, $log_data, $user_id, $action, $source, $raffle_logger)
{
// prepare raffle log entry
$raffle_data = [
'user_id' => $user_id,
'serial' => $serial,
'warranty_id' => null,
'action' => '',
'bmodel_name' => '',
'bsize_name' => '',
'first_name' => '',
'last_name' => '',
'plate_number' => '',
'contact_num' => '',
'email' => '',
'address' => '',
];
// get serial
$warr_serial = $this->em->getRepository(WarrantySerial::class)->find($serial);
if ($warr_serial == null) {
return [
'success' => false,
'error' => 'Invalid warranty serial code.',
];
}
// check if warranty exists already
$warr = $this->em->getRepository(Warranty::class)->findOneBy(['serial' => $serial]);
// skip warranty if it already exists
if ($warr != null) {
/*
// NOTE: we could not update in the old version
$res->setError(true)
->setErrorMessage('Warranty registration entry already exists.');
return $res;
*/
// check if warranty is registered to a serial owned by customer
$warr_plate = $warr->getPlateNumber();
$cust = $this->session->getCustomer();
$is_customer_warranty = $this->checkCustomerPlateNumber($warr_plate, $cust);
if (!$is_customer_warranty) {
$error_msg = 'Warranty registered to a vehicle not in your list of vehicles.';
// set action to update
$action = 'update';
$logger->logWarrantyInfo($log_data, $error_msg, $user_id, $action, $source);
// response
return [
'success' => false,
'error' => $error_msg,
];
}
$sms_msg = $trans->trans('warranty_update_confirm');
// update raffle data action
$raffle_data['action'] = 'warranty_update';
} else {
$warr = new Warranty();
$sms_msg = $trans->trans('warranty_register_confirm');
// set warranty source
$warr->setCreateSource($source);
// update raffle data action
$raffle_data['action'] = 'warranty_create';
}
// get sap battery
$sku = $warr_serial->getSKU();
$sap_bty = null;
if ($sku != null) {
$sap_bty = $this->em->getRepository(SAPBattery::class)->find($sku);
if ($sap_bty == null) {
$error_msg = 'Could not find battery entry for warranty.';
$logger->logWarrantyInfo($log_data, $error_msg, $user_id, $action, $source);
// response
return [
'success' => false,
'error' => $error_msg,
];
}
}
// default date purchase to today
// NOTE: might need to change this later
$date_pur = new DateTime();
// get date purchase specified by customer
$date_pur_cust = DateTime::createFromFormat('Y-m-d', $req->request->get('date_purchase'));
if (!$date_pur_cust) {
$error_msg = 'Invalid date format for date of purchase.';
$logger->logWarrantyInfo($log_data, $error_msg, $user_id, $action, $source);
// response
return [
'success' => false,
'error' => $error_msg,
];
}
$customer = $this->session->getCustomer();
if ($customer != null) {
$warr->setCustomer($customer);
// get customer vehicles
$vehicle = $this->findCustomerVehicle($customer, $req->request->get('plate_number'));
if ($vehicle != null)
$warr->setVehicle($vehicle);
}
// TODO: make a standard clean plate number service
// clean plate number
$plate = $req->request->get('plate_number');
// upper case and remove spaces
$plate = strtoupper(str_replace(' ', '', $plate));
// remove special characters
$plate = preg_replace('/[^A-Za-z0-9. -]/', '', $plate);
// create or update warranty entry
$warr->setSerial($serial)
->setFirstName($req->request->get('first_name'))
->setLastName($req->request->get('last_name'))
->setEmail($req->request->get('email'))
->setPlateNumber($plate)
// TODO: figure out how to compute date of purchase
->setDatePurchase($date_pur)
// TODO: set status
// ->setStatus()
// TODO: set battery model and size id
// ->setBatterySize()
// ->setBatteryModel()
->setSAPBattery($sap_bty)
->setMobileNumber(substr($this->session->getPhoneNumber(), 2))
->setActivated(true)
// files
->setFileInvoice($inv_filename)
->setFileWarrantyCard($wcard_filename)
// new fields
->setOdometer($req->request->get('odometer', 0))
->setDatePurchaseCustomer($date_pur_cust)
->setContactNumber($req->request->get('contact_num'))
->setCustomerAddress($req->request->get('cust_address'))
->setDealerName($req->request->get('dealer_name'))
->setDealerAddress($req->request->get('dealer_address'))
->setDealerBranchCode($req->request->get('branch_code'))
->setValidated(false);
// TODO: check for date purchase and date expire
$this->em->persist($warr);
// TODO: check if we need to do anyting else
$logger->logWarrantyInfo($log_data, '', $user_id, $action, $source);
// send sms
// error_log('sending sms to - ' . $this->session->getPhoneNumber());
$rt->sendSMS($this->session->getPhoneNumber(), $trans->trans('message.battery_brand_allcaps'), $sms_msg);
// prepare the rest of the raffle log entry
$raffle_data['warranty_id'] = $warr->getID();
$raffle_data['bmodel_name'] = $sap_bty->getBrand()->getName();
$raffle_data['bsize_name'] = $sap_bty->getSize()->getName();
$raffle_data['first_name'] = $req->request->get('first_name', '');
$raffle_data['last_name'] = $req->request->get('last_name', '');
$raffle_data['plate_number'] = $plate;
$raffle_data['contact_num'] = $req->request->get('contact_num', '');
$raffle_data['email'] = $req->request->get('email', '');
$raffle_data['address'] = $req->request->get('cust_address', '');
$data_sent = [
'plate_number' => $req->request->get('plate_number'),
'first_name' => $req->request->get('first_name'),
'last_name' => $req->request->get('last_name'),
'date_purchase' => $req->request->get('date_purchase'),
'address' => $req->request->get('cust_address', ''),
'email' => $req->request->get('email', ''),
'contact_num' => $req->request->get('contact_num', ''),
];
// log raffle data
$raffle_logger->logRaffleInfo($data_sent, $raffle_data);
// response
return [
'success' => true,
];
}
protected function findCustomerVehicle($customer, $plate_number)
{
$clean_plate = Warranty::cleanPlateNumber($plate_number);
if ($clean_plate) {
// find the customer vehicle and get the vehicle
$cv = $this->em->getRepository(CustomerVehicle::class)->findOneBy(['plate_number' => $clean_plate, 'customer' => $customer]);
if ($cv != null) {
$vehicle = $cv->getVehicle();
return $vehicle;
}
}
return null;
}
}

View file

@ -0,0 +1,253 @@
<?php
namespace App\Controller;
use App\Entity\CustomerLocation;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Catalyst\MenuBundle\Annotation\Menu;
class CustomerLocationController extends Controller
{
/**
* @Menu(selected="customer_location_list")
* @IsGranted("cust_location.list")
*/
public function index()
{
return $this->render('customer-location/list.html.twig');
}
/**
* @IsGranted("cust_location.list")
*/
public function datatableRows(Request $req)
{
// get query builder
$qb = $this->getDoctrine()
->getRepository(CustomerLocation::class)
->createQueryBuilder('q');
// get datatable params
$datatable = $req->request->get('datatable');
// count total records
$tquery = $qb->select('COUNT(q)');
$this->setQueryFilters($datatable, $tquery);
$total = $tquery->getQuery()
->getSingleScalarResult();
// get current page number
$page = $datatable['pagination']['page'] ?? 1;
$perpage = $datatable['pagination']['perpage'];
$offset = ($page - 1) * $perpage;
// add metadata
$meta = [
'page' => $page,
'perpage' => $perpage,
'pages' => ceil($total / $perpage),
'total' => $total,
'sort' => 'asc',
'field' => 'id'
];
// build query
$query = $qb->select('q');
$this->setQueryFilters($datatable, $query);
// check if sorting is present, otherwise use default
if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) {
$order = $datatable['sort']['sort'] ?? 'asc';
$query->orderBy('q.' . $datatable['sort']['field'], $order);
} else {
$query->orderBy('q.id', 'asc');
}
// get rows for this page
$obj_rows = $query->setFirstResult($offset)
->setMaxResults($perpage)
->getQuery()
->getResult();
// process rows
$rows = [];
foreach ($obj_rows as $orow) {
// add row data
$row['id'] = $orow->getID();
$row['name'] = $orow->getName();
// add row metadata
$row['meta'] = [
'update_url' => '',
'delete_url' => ''
];
// add crud urls
if ($this->isGranted('cust_location.update'))
$row['meta']['update_url'] = $this->generateUrl('customer_location_update_form', ['id' => $row['id']]);
if ($this->isGranted('cust_location.delete'))
$row['meta']['delete_url'] = $this->generateUrl('customer_location_delete', ['id' => $row['id']]);
$rows[] = $row;
}
// response
return $this->json([
'meta' => $meta,
'data' => $rows
]);
}
/**
* @Menu(selected="customer_location.list")
* @IsGranted("cust_location.add")
*/
public function addForm()
{
$cust_location = new CustomerLocation();
$params = [
'cust_location' => $cust_location,
'mode' => 'create',
];
// response
return $this->render('customer-location/form.html.twig', $params);
}
/**
* @IsGranted("cust_location.add")
*/
public function addSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator)
{
$cust_location = new CustomerLocation();
$this->setObject($cust_location, $req);
// validate
$errors = $validator->validate($cust_location);
// initialize error list
$error_array = [];
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// validated! save the entity
$em->persist($cust_location);
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!'
]);
}
/**
* @Menu(selected="customer_location_list")
* @ParamConverter("cust_location", class="App\Entity\CustomerLocation")
* @IsGranted("cust_location.update")
*/
public function updateForm($id, EntityManagerInterface $em, CustomerLocation $cust_location)
{
$params = [];
$params['cust_location'] = $cust_location;
$params['mode'] = 'update';
// response
return $this->render('customer-location/form.html.twig', $params);
}
/**
* @ParamConverter("cust_location", class="App\Entity\CustomerLocation")
* @IsGranted("cust_location.update")
*/
public function updateSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator, CustomerLocation $cust_location)
{
$this->setObject($cust_location, $req);
// validate
$errors = $validator->validate($cust_location);
// initialize error list
$error_array = [];
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// validated! save the entity
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!'
]);
}
/**
* @ParamConverter("cust_location", class="App\Entity\CustomerLocation")
* @IsGranted("cust_location.update")
*/
public function deleteSubmit(EntityManagerInterface $em, CustomerLocation $cust_location)
{
// delete this object
$em->remove($cust_location);
$em->flush();
// response
$response = new Response();
$response->setStatusCode(Response::HTTP_OK);
$response->send();
}
protected function setObject(CustomerLocation $obj, Request $req)
{
// set and save values
$obj->setName($req->request->get('name'))
->setCode($req->request->get('code'));
}
protected function setQueryFilters($datatable, QueryBuilder $query)
{
if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) {
$query->where('q.name LIKE :filter')
->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%');
}
}
}

View file

@ -0,0 +1,129 @@
<?php
namespace App\Controller;
use App\Ramcar\InsuranceApplicationStatus;
use App\Service\FCMSender;
use App\Service\InsuranceConnector;
use App\Entity\InsuranceApplication;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use DateTime;
class InsuranceController extends Controller
{
protected $ic;
protected $em;
protected $fcmclient;
public function __construct(InsuranceConnector $ic, EntityManagerInterface $em, FCMSender $fcmclient)
{
$this->ic = $ic;
$this->em = $em;
$this->fcmclient = $fcmclient;
}
public function listen(Request $req)
{
$payload = $req->request->all();
// log this callback
$this->ic->log('CALLBACK', "[]", json_encode($payload), 'callback');
// if no transaction code given, silently fail
if (empty($payload['transaction_code'])) {
error_log("Invalid insurance callback received: " . print_r($payload, true));
return $this->json([
'success' => true,
]);
}
// get event type and process accordingly
$event_name = $payload['transaction_code'];
switch ($event_name) {
case 'GR002':
return $this->handleAuthenticated($payload);
break;
case 'GR003':
return $this->handleUpdateMade($payload);
break;
default:
break;
}
return $this->json([
'success' => true,
'payload' => $payload,
]);
}
protected function handleAuthenticated($payload)
{
$obj = $this->getApplication($payload['id']);
$now = new DateTime();
$expiry = DateTime::createFromFormat("Y-m-d", $payload['expiry_date']);
if (!empty($obj)) {
// mark as completed
$obj->setStatus(InsuranceApplicationStatus::COMPLETED);
$obj->setDateComplete($now);
$obj->setDateExpire($expiry);
$obj->setCOC($payload['coc_url']);
$this->em->flush();
// send notification
$this->fcmclient->sendEvent($obj->getCustomer(), "insurance_fcm_title_completed", "insurance_fcm_body_completed", [
'cv_id' => $obj->getCustomerVehicle()->getID(),
]);
}
return $this->json([
'success' => true,
]);
}
protected function handleUpdateMade($payload)
{
$obj = $this->getApplication($payload['id']);
if (!empty($obj)) {
$metadata = $obj->getMetadata();
// initialize change list if not present
if (empty($metadata['changes'])) {
$metadata['changes'] = [];
}
$now = new DateTime;
$metadata['changes'][$now->format('Y-m-d H:i:s')] = $payload['data'];
// update metadata to record change
$obj->setMetadata($metadata);
$this->em->flush();
// send notification
$this->fcmclient->sendEvent($obj->getCustomer(), "insurance_fcm_title_updated", "insurance_fcm_body_updated", [
'cv_id' => $obj->getCustomerVehicle()->getID(),
]);
}
return $this->json([
'success' => true,
]);
}
protected function getApplication($transaction_id)
{
$result = $this->em->getRepository(InsuranceApplication::class)->findBy([
'ext_transaction_id' => $transaction_id,
], [], 1);
return !empty($result) ? $result[0] : false;
}
}

View file

@ -0,0 +1,269 @@
<?php
namespace App\Controller;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Catalyst\MenuBundle\Annotation\Menu;
use App\Entity\PriceTier;
use App\Entity\Battery;
use App\Entity\ServiceOffering;
use App\Entity\ItemType;
use App\Entity\ItemPrice;
class ItemPricingController extends Controller
{
/**
* @Menu(selected="item_pricing")
* @IsGranted("item_pricing.update")
*/
public function index (EntityManagerInterface $em)
{
// get all the price tiers
$price_tiers = $em->getRepository(PriceTier::class)->findAll();
// get all item types
$item_types = $em->getRepository(ItemType::class)->findBy([], ['name' => 'asc']);
// get all the items/batteries
// load only batteries upon initial loading
$items = $this->getBatteries($em);
// set the default item type to battery
$default_it = $em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']);
$params = [
'sets' => [
'price_tiers' => $price_tiers,
'item_types' => $item_types,
],
'items' => $items,
'default_item_type_id' => $default_it->getID(),
];
return $this->render('item-pricing/form.html.twig', $params);
}
/**
* @Menu(selected="item_pricing")
* @IsGranted("item_pricing.update")
*/
public function formSubmit(Request $req, EntityManagerInterface $em)
{
$pt_id = $req->request->get('price_tier_id');
$it_id = $req->request->get('item_type_id');
$prices = $req->request->get('price');
// get the item type
$item_type = $em->getRepository(ItemType::class)->find($it_id);
if ($item_type->getCode() == 'battery')
{
// get batteries
$items = $em->getRepository(Battery::class)->findBy(['flag_active' => true], ['id' => 'asc']);
}
else
{
// get service offerings
$items = $em->getRepository(ServiceOffering::class)->findBy([], ['id' => 'asc']);
}
// on default price tier
if ($pt_id == 0)
{
// default price tier, update battery or service offering, depending on item type
// NOTE: battery and service offering prices or fees are stored as decimal.
foreach ($items as $item)
{
$item_id = $item->getID();
if (isset($prices[$item_id]))
{
// check item type
if ($item_type->getCode() == 'battery')
$item->setSellingPrice($prices[$item_id]);
else
$item->setFee($prices[$item_id]);
}
}
}
else
{
// get the price tier
$price_tier = $em->getRepository(PriceTier::class)->find($pt_id);
$item_prices = $price_tier->getItemPrices();
// clear the tier's item prices for the specific item type
foreach ($item_prices as $ip)
{
if ($ip->getItemType() == $item_type)
$em->remove($ip);
}
// update the tier's item prices
foreach ($items as $item)
{
$item_id = $item->getID();
$item_price = new ItemPrice();
$item_price->setItemType($item_type)
->setPriceTier($price_tier)
->setItemID($item_id);
if (isset($prices[$item_id]))
{
$item_price->setPrice($prices[$item_id] * 100);
}
else
{
$item_price->setPrice($item->getPrice() * 100);
}
// save
$em->persist($item_price);
}
}
$em->flush();
return $this->redirectToRoute('item_pricing');
}
/**
* @IsGranted("item_pricing.update")
*/
public function itemPrices(EntityManagerInterface $em, $pt_id, $it_id)
{
$pt_prices = [];
// get the item type
$it = $em->getRepository(ItemType::class)->find($it_id);
// check if default prices are needed
if ($pt_id != 0)
{
// get the price tier
$pt = $em->getRepository(PriceTier::class)->find($pt_id);
// get the items under the price tier
$pt_items = $pt->getItemPrices();
foreach ($pt_items as $pt_item)
{
// make item price hash
$pt_prices[$pt_item->getItemID()] = $pt_item->getPrice();
}
}
// get the prices from battery or service offering, depending on item type
if ($it->getCode() == 'battery')
{
// get batteries
$items = $em->getRepository(Battery::class)->findBy(['flag_active' => true], ['id' => 'asc']);
}
else
{
// get service offerings
$items = $em->getRepository(ServiceOffering::class)->findBy([], ['id' => 'asc']);
}
$data_items = [];
foreach ($items as $item)
{
$item_id = $item->getID();
// get default price
if ($it->getCode() == 'battery')
{
$price = $item->getSellingPrice();
$name = $item->getModel()->getName() . ' ' . $item->getSize()->getName();
}
else
{
$price = $item->getFee();
$name = $item->getName();
}
// check if tier has price for item
if (isset($pt_prices[$item_id]))
{
$pt_price = $pt_prices[$item_id];
// actual price
$price = number_format($pt_price / 100, 2, '.', '');
}
$actual_price = $price;
$data_items[] = [
'id' => $item_id,
'name' => $name,
'item_type_id' => $it->getID(),
'item_type' => $it->getName(),
'price' => $actual_price,
];
}
// response
return new JsonResponse([
'items' => $data_items,
]);
}
protected function getBatteries(EntityManagerInterface $em)
{
// get the item type for battery
$batt_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'battery']);
// get all active batteries
$batts = $em->getRepository(Battery::class)->findBy(['flag_active' => true], ['id' => 'asc']);
foreach ($batts as $batt)
{
$batt_set[$batt->getID()] = [
'name' => $batt->getModel()->getName() . ' ' . $batt->getSize()->getName(),
'item_type_id' => $batt_item_type->getID(),
'item_type' => $batt_item_type->getName(),
'price' => $batt->getSellingPrice(),
];
}
return [
'items' => $batt_set,
];
}
protected function getServiceOfferings(EntityeManagerInterface $em)
{
// get the item type for service offering
$service_item_type = $em->getRepository(ItemType::class)->findOneBy(['code' => 'service_offering']);
// get all service offerings
$services = $em->getRepository(ServiceOffering::class)->findBy([], ['id' => 'asc']);
$service_set = [];
foreach ($services as $service)
{
$service_set[$service->getID()] = [
'name' => $service->getName(),
'item_type_id' => $service_item_type->getID(),
'item_type' => $service_item_type->getName(),
'price' => $service->getFee(),
];
}
return [
'items' => $service_set,
];
}
}

View file

@ -0,0 +1,251 @@
<?php
namespace App\Controller;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use App\Entity\ItemType;
use Catalyst\MenuBundle\Annotation\Menu;
class ItemTypeController extends Controller
{
/**
* @Menu(selected="item_type_list")
* @IsGranted("item_type.list")
*/
public function index ()
{
return $this->render('item-type/list.html.twig');
}
/**
* @IsGranted("item_type.list")
*/
public function datatableRows(Request $req)
{
// get query builder
$qb = $this->getDoctrine()
->getRepository(ItemType::class)
->createQueryBuilder('q');
// get datatable params
$datatable = $req->request->get('datatable');
// count total records
$tquery = $qb->select('COUNT(q)');
$this->setQueryFilters($datatable, $tquery);
$total = $tquery->getQuery()
->getSingleScalarResult();
// get current page number
$page = $datatable['pagination']['page'] ?? 1;
$perpage = $datatable['pagination']['perpage'];
$offset = ($page - 1) * $perpage;
// add metadata
$meta = [
'page' => $page,
'perpage' => $perpage,
'pages' => ceil($total / $perpage),
'total' => $total,
'sort' => 'asc',
'field' => 'id'
];
// build query
$query = $qb->select('q');
$this->setQueryFilters($datatable, $query);
// check if sorting is present, otherwise use default
if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) {
$order = $datatable['sort']['sort'] ?? 'asc';
$query->orderBy('q.' . $datatable['sort']['field'], $order);
} else {
$query->orderBy('q.id', 'asc');
}
// get rows for this page
$obj_rows = $query->setFirstResult($offset)
->setMaxResults($perpage)
->getQuery()
->getResult();
// process rows
$rows = [];
foreach ($obj_rows as $orow) {
// add row data
$row['id'] = $orow->getID();
$row['name'] = $orow->getName();
// add row metadata
$row['meta'] = [
'update_url' => '',
'delete_url' => ''
];
// add crud urls
if ($this->isGranted('item_type.update'))
$row['meta']['update_url'] = $this->generateUrl('item_type_update_form', ['id' => $row['id']]);
if ($this->isGranted('item_type.delete'))
$row['meta']['delete_url'] = $this->generateUrl('item_type_delete', ['id' => $row['id']]);
$rows[] = $row;
}
// response
return $this->json([
'meta' => $meta,
'data' => $rows
]);
}
/**
* @Menu(selected="item_type.list")
* @IsGranted("item_type.add")
*/
public function addForm()
{
$item_type = new ItemType();
$params = [
'obj' => $item_type,
'mode' => 'create',
];
// response
return $this->render('item-type/form.html.twig', $params);
}
/**
* @IsGranted("item_type.add")
*/
public function addSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator)
{
$item_type = new ItemType();
$this->setObject($item_type, $req);
// validate
$errors = $validator->validate($item_type);
// initialize error list
$error_array = [];
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// validated! save the entity
$em->persist($item_type);
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!'
]);
}
/**
* @Menu(selected="item_type_list")
* @ParamConverter("item_type", class="App\Entity\ItemType")
* @IsGranted("item_type.update")
*/
public function updateForm($id, EntityManagerInterface $em, ItemType $item_type)
{
$params = [];
$params['obj'] = $item_type;
$params['mode'] = 'update';
// response
return $this->render('item-type/form.html.twig', $params);
}
/**
* @ParamConverter("item_type", class="App\Entity\ItemType")
* @IsGranted("item_type.update")
*/
public function updateSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator, ItemType $item_type)
{
$this->setObject($item_type, $req);
// validate
$errors = $validator->validate($item_type);
// initialize error list
$error_array = [];
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// validated! save the entity
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!'
]);
}
/**
* @ParamConverter("item_type", class="App\Entity\ItemType")
* @IsGranted("item_type.delete")
*/
public function deleteSubmit(EntityManagerInterface $em, ItemType $item_type)
{
// delete this object
$em->remove($item_type);
$em->flush();
// response
$response = new Response();
$response->setStatusCode(Response::HTTP_OK);
$response->send();
}
protected function setObject(ItemType $obj, Request $req)
{
// set and save values
$obj->setName($req->request->get('name'))
->setCode($req->request->get('code'));
}
protected function setQueryFilters($datatable, QueryBuilder $query)
{
if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) {
$query->where('q.name LIKE :filter')
->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%');
}
}
}

View file

@ -7,6 +7,7 @@ use App\Ramcar\InvoiceCriteria;
use App\Ramcar\CMBServiceType;
use App\Ramcar\ServiceType;
use App\Ramcar\JOCancelReasons;
use App\Ramcar\TransactionOrigin;
use App\Entity\CustomerVehicle;
use App\Entity\Promo;
@ -21,16 +22,17 @@ use App\Service\JobOrderHandlerInterface;
use App\Service\GISManagerInterface;
use App\Service\MapTools;
use App\Service\MQTTClient;
use App\Service\MQTTClientApiv2;
use App\Service\FCMSender;
use App\Service\APNSClient;
use App\Service\InventoryManager;
use App\Service\HubSelector;
use App\Service\RiderTracker;
use App\Service\MotivConnector;
use App\Service\PriceTierManager;
use App\Service\GeofenceTracker;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
@ -41,6 +43,8 @@ use Doctrine\ORM\EntityManagerInterface;
use Catalyst\MenuBundle\Annotation\Menu;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
class JobOrderController extends Controller
{
public function getJobOrders(Request $req, JobOrderHandlerInterface $jo_handler)
@ -299,6 +303,7 @@ class JobOrderController extends Controller
$rows[$key]['meta']['update_url'] = $this->generateUrl($jo_handler->getEditRoute($jo_id, $tier_params['edit_route']), ['id' => $jo_id]);
$rows[$key]['meta']['onestep_edit_url'] = $this->generateUrl('jo_onestep_edit_form', ['id' => $jo_id]);
$rows[$key]['meta']['pdf_url'] = $this->generateUrl('jo_pdf_form', ['id' => $jo_id]);
$rows[$key]['meta']['view_url'] = $this->generateUrl('jo_all_view_form',['id' => $jo_id]);
}
if ($tier_params['unlock_route'] != '')
@ -355,7 +360,7 @@ class JobOrderController extends Controller
return $this->render($template, $params);
}
public function processingSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, $id)
public function processingSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient, $id)
{
$this->denyAccessUnlessGranted('jo_proc.list', null, 'No access.');
@ -363,7 +368,7 @@ class JobOrderController extends Controller
$error_array = [];
try
{
$error_array = $jo_handler->dispatchJobOrder($req, $id, $mclient);
$error_array = $jo_handler->dispatchJobOrder($req, $id, $mclient, $mclientv2, $fcmclient);
}
catch (AccessDeniedHttpException $e)
{
@ -422,7 +427,7 @@ class JobOrderController extends Controller
}
public function assigningSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTCLient $mclient, APNSClient $aclient, $id)
public function assigningSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTCLient $mclient, FCMSender $fcmclient, APNSClient $aclient, $id)
{
$this->denyAccessUnlessGranted('jo_assign.list', null, 'No access.');
@ -482,7 +487,7 @@ class JobOrderController extends Controller
return $this->render($template, $params);
}
public function fulfillmentSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, $id)
public function fulfillmentSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, FCMSender $fcmclient, $id)
{
$this->denyAccessUnlessGranted('jo_fulfill.list', null, 'No access.');
@ -541,7 +546,7 @@ class JobOrderController extends Controller
return $this->render($template, $params);
}
public function openHubSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, $id)
public function openHubSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient, $id)
{
$this->denyAccessUnlessGranted('jo_open.list', null, 'No access.');
@ -550,7 +555,7 @@ class JobOrderController extends Controller
try
{
$error_array = $jo_handler->setHub($req, $id, $mclient);
$error_array = $jo_handler->setHub($req, $id, $mclient, $mclientv2, $fcmclient);
}
catch (NotFoundHttpException $e)
{
@ -599,7 +604,7 @@ class JobOrderController extends Controller
return $this->render($template, $params);
}
public function openRiderSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, $id)
public function openRiderSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient, $id)
{
$this->denyAccessUnlessGranted('jo_open.list', null, 'No access.');
@ -608,7 +613,7 @@ class JobOrderController extends Controller
try
{
$error_array = $jo_handler->setRider($req, $id, $mclient);
$error_array = $jo_handler->setRider($req, $id, $mclient, $mclientv2, $fcmclient);
}
catch (NotFoundHttpException $e)
{
@ -658,6 +663,32 @@ class JobOrderController extends Controller
return $this->render($template, $params);
}
/**
* @Menu(selected="jo_all")
*/
public function allViewForm($id, JobOrderHandlerInterface $jo_handler,
GISManagerInterface $gis, EntityManagerInterface $em)
{
$this->denyAccessUnlessGranted('jo_all.list', null, 'No access.');
try
{
$params = $jo_handler->initializeAllViewForm($id);
}
catch (NotFoundHttpException $e)
{
throw $this->createNotFoundException($e->getMessage());
}
$params['return_url'] = $this->generateUrl('jo_all');
$params['map_js_file'] = $gis->getJSJOFile();
$template = $params['template'];
// response
return $this->render($template, $params);
}
public function pdfForm(Request $req, $id, JobOrderHandlerInterface $jo_handler)
{
@ -682,7 +713,7 @@ class JobOrderController extends Controller
]);
}
public function cancelJobOrder(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, $id)
public function cancelJobOrder(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient, $id)
{
$this->denyAccessUnlessGranted('joborder.cancel', null, 'No access.');
@ -699,7 +730,7 @@ class JobOrderController extends Controller
try
{
$jo_handler->cancelJobOrder($req, $id, $mclient);
$jo_handler->cancelJobOrder($req, $id, $mclient, $mclientv2, $fcmclient);
}
catch (NotFoundHttpException $e)
{
@ -713,7 +744,7 @@ class JobOrderController extends Controller
}
public function generateInvoice(Request $req, InvoiceGeneratorInterface $ic)
public function generateInvoice(Request $req, InvoiceGeneratorInterface $ic, PriceTierManager $pt_manager)
{
// error_log('generating invoice...');
$error = false;
@ -723,6 +754,21 @@ class JobOrderController extends Controller
$promo_id = $req->request->get('promo');
$cvid = $req->request->get('cvid');
$service_charges = $req->request->get('service_charges', []);
$flag_coolant = $req->request->get('flag_coolant', false);
$flag_sealant = $req->request->get('flag_sealant', false);
// coordinates
// need to check if lng and lat are set
$lng = $req->request->get('coord_lng', 0);
$lat = $req->request->get('coord_lat', 0);
$price_tier = 0;
if (!empty($lng) && !empty($lat))
{
$coordinates = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
$price_tier = $pt_manager->getPriceTier($coordinates);
}
$em = $this->getDoctrine()->getManager();
@ -737,8 +783,12 @@ class JobOrderController extends Controller
// instantiate invoice criteria
$criteria = new InvoiceCriteria();
$criteria->setServiceType($stype)
->setCustomerVehicle($cv);
->setCustomerVehicle($cv)
->setIsTaxable()
->setSource(TransactionOrigin::CALL)
->setPriceTier($price_tier)
->setHasCoolant($flag_coolant)
->setHasSealant($flag_sealant);
/*
// if it's a jumpstart or troubleshoot only, we know what to charge already

View file

@ -0,0 +1,304 @@
<?php
namespace App\Controller;
use App\Entity\MotoliteEvent;
use App\Ramcar\MotoliteEventType;
use App\Service\FileUploader;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use DateTime;
use Catalyst\MenuBundle\Annotation\Menu;
class MotoliteEventController extends Controller
{
/**
* @Menu(selected="motolite_event_list")
*/
public function index()
{
$this->denyAccessUnlessGranted('motolite_event.list', null, 'No access.');
return $this->render('motolite-event/list.html.twig');
}
public function rows(Request $req)
{
$this->denyAccessUnlessGranted('motolite_event.list', null, 'No access.');
// get query builder
$qb = $this->getDoctrine()
->getRepository(MotoliteEvent::class)
->createQueryBuilder('q');
// get datatable params
$datatable = $req->request->get('datatable');
// count total records
$tquery = $qb->select('COUNT(q)');
$this->setQueryFilters($datatable, $tquery);
$total = $tquery->getQuery()
->getSingleScalarResult();
// get current page number
$page = $datatable['pagination']['page'] ?? 1;
$perpage = $datatable['pagination']['perpage'];
$offset = ($page - 1) * $perpage;
// add metadata
$meta = [
'page' => $page,
'perpage' => $perpage,
'pages' => ceil($total / $perpage),
'total' => $total,
'sort' => 'asc',
'field' => 'id'
];
// build query
$query = $qb->select('q');
$this->setQueryFilters($datatable, $query);
// check if sorting is present, otherwise use default
if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) {
$order = $datatable['sort']['sort'] ?? 'asc';
$query->orderBy('q.' . $datatable['sort']['field'], $order);
} else {
$query->orderBy('q.event_time', 'asc');
}
// get rows for this page
$obj_rows = $query->setFirstResult($offset)
->setMaxResults($perpage)
->getQuery()
->getResult();
// process rows
$rows = [];
foreach ($obj_rows as $orow) {
// add row data
$row['id'] = $orow->getID();
$row['name'] = $orow->getName();
$row['event_type'] = MotoliteEventType::getName($orow->getEventType());
$row['event_time'] = $orow->getEventTime()->format("d M Y g:i A");
$row['url'] = '<a href="' . $orow->getUrl() . '" target="_blank">Link</a>';
$row['image_file'] = $orow->getImageFile();
// add row metadata
$row['meta'] = [
'update_url' => '',
'delete_url' => ''
];
// add crud urls
if ($this->isGranted('motolite_event.update'))
$row['meta']['update_url'] = $this->generateUrl('motolite_event_update', ['id' => $row['id']]);
if ($this->isGranted('motolite_event.delete'))
$row['meta']['delete_url'] = $this->generateUrl('motolite_event_delete', ['id' => $row['id']]);
$rows[] = $row;
}
// response
return $this->json([
'meta' => $meta,
'data' => $rows
]);
}
/**
* @Menu(selected="motolite_event_list")
*/
public function addForm()
{
$this->denyAccessUnlessGranted('motolite_event.add', null, 'No access.');
$params = [];
$params['obj'] = new MotoliteEvent();
$params['mode'] = 'create';
$params['event_types'] = MotoliteEventType::getCollection();
$em = $this->getDoctrine()->getManager();
// response
return $this->render('motolite-event/form.html.twig', $params);
}
public function addSubmit(Request $req, EncoderFactoryInterface $ef, ValidatorInterface $validator)
{
$this->denyAccessUnlessGranted('motolite_event.add', null, 'No access.');
// create new object
$em = $this->getDoctrine()->getManager();
$obj = new MotoliteEvent();
$this->setObject($obj, $req);
// validate
$errors = $validator->validate($obj);
// initialize error list
$error_array = [];
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// validated! save the entity
$em->persist($obj);
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!'
]);
}
/**
* @Menu(selected="motolite_event_list")
*/
public function updateForm($id)
{
$this->denyAccessUnlessGranted('motolite_event.update', null, 'No access.');
// get row data
$em = $this->getDoctrine()->getManager();
$obj = $em->getRepository(MotoliteEvent::class)->find($id);
// make sure this row exists
if (empty($obj))
throw $this->createNotFoundException('The item does not exist');
$params = [];
$params['obj'] = $obj;
$params['mode'] = 'update';
$params['event_types'] = MotoliteEventType::getCollection();
// response
return $this->render('motolite-event/form.html.twig', $params);
}
public function updateSubmit(Request $req, EncoderFactoryInterface $ef, ValidatorInterface $validator, $id)
{
$this->denyAccessUnlessGranted('motolite_event.update', null, 'No access.');
// get object data
$em = $this->getDoctrine()->getManager();
$obj = $em->getRepository(MotoliteEvent::class)->find($id);
// make sure this object exists
if (empty($obj))
throw $this->createNotFoundException('The item does not exist');
$this->setObject($obj, $req);
// validate
$errors = $validator->validate($obj);
// initialize error list
$error_array = [];
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// validated! save the entity
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!'
]);
}
public function destroy($id)
{
$this->denyAccessUnlessGranted('motolite_event.delete', null, 'No access.');
// get object data
$em = $this->getDoctrine()->getManager();
$obj = $em->getRepository(MotoliteEvent::class)->find($id);
if (empty($obj))
throw $this->createNotFoundException('The item does not exist');
// delete this object
$em->remove($obj);
$em->flush();
// response
$response = new Response();
$response->setStatusCode(Response::HTTP_OK);
$response->send();
}
protected function setQueryFilters($datatable, QueryBuilder $query)
{
if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) {
$query->where('q.name LIKE :filter')
->orWhere('q.url LIKE :filter')
->orWhere('q.event_type LIKE :filter')
->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%');
}
}
protected function setObject(MotoliteEvent $obj, Request $req)
{
// times
$format = "Y-m-d\TH:i";
$event_time = DateTime::createFromFormat($format, $req->request->get('event_time'));
$event_type = $req->request->get('event_type');
// set and save values
$obj->setName($req->request->get('name'))
->setEventType($event_type)
->setEventTime($event_time)
->setUrl($req->request->get('url'))
->setImageFile($req->request->get('image_file'));
}
public function uploadImage(Request $req, FileUploader $uploader)
{
// retrieve temporary info for file
$file = $req->files->get('image_file');
// upload the file
$filename = $uploader->upload($file);
// return response
return $this->json([
'success' => true,
'filename' => $filename
]);
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace App\Controller;
use App\Entity\GatewayTransaction;
use App\Ramcar\TransactionStatus;
use App\Service\PayMongoConnector;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use DateTime;
class PayMongoController extends Controller
{
protected $pm;
protected $em;
public function __construct(PayMongoConnector $pm, EntityManagerInterface $em)
{
$this->pm = $pm;
$this->em = $em;
}
public function listen(Request $req)
{
$payload = json_decode($req->getContent(), true);
// log this callback
$this->pm->log('CALLBACK', "[]", $req->getContent(), 'callback');
// if no event type given, silently fail
if (empty($payload['data'])) {
error_log("Invalid paymongo callback received: " . print_r($payload, true));
return $this->json([
'success' => true,
]);
}
// get event type and process accordingly
$attr = $payload['data']['attributes'];
$event = $attr['data'];
$event_name = $attr['type'];
switch ($event_name) {
case "payment.paid":
return $this->handlePaymentPaid($event);
case "payment.failed":
return $this->handlePaymentFailed($event);
case "payment.refunded": // TODO: handle refunds
case "payment.refund.updated":
case "checkout_session.payment.paid":
default:
break;
}
return $this->json([
'success' => true,
]);
}
protected function handlePaymentPaid($event)
{
$metadata = $event['attributes']['metadata'];
$obj = $this->getTransaction($metadata['transaction_id']);
if (!empty($obj)) {
// mark as paid
$obj->setStatus(TransactionStatus::PAID);
$obj->setDatePay(new DateTime());
$this->em->flush();
}
return $this->json([
'success' => true,
]);
}
protected function handlePaymentFailed($event)
{
// TODO: do something about failed payments?
return $this->json([
'success' => true,
]);
}
protected function getTransaction($id)
{
//$class_name = 'App\\Entity\\' . $type;
//$instance = new $class_name;
return $this->em->getRepository(GatewayTransaction::class)->find($id);
}
public function paymentSuccess(Request $req)
{
return $this->render('paymongo/success.html.twig');
}
public function paymentCancelled(Request $req)
{
return $this->render('paymongo/cancelled.html.twig');
}
}

View file

@ -0,0 +1,355 @@
<?php
namespace App\Controller;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Catalyst\MenuBundle\Annotation\Menu;
use App\Entity\PriceTier;
use App\Entity\SupportedArea;
class PriceTierController extends Controller
{
/**
* @Menu(selected="price_tier_list")
* @IsGranted("price_tier.list")
*/
public function index()
{
return $this->render('price-tier/list.html.twig');
}
/**
* @IsGranted("price_tier.list")
*/
public function datatableRows(Request $req)
{
// get query builder
$qb = $this->getDoctrine()
->getRepository(PriceTier::class)
->createQueryBuilder('q');
// get datatable params
$datatable = $req->request->get('datatable');
// count total records
$tquery = $qb->select('COUNT(q)');
$this->setQueryFilters($datatable, $tquery);
$total = $tquery->getQuery()
->getSingleScalarResult();
// get current page number
$page = $datatable['pagination']['page'] ?? 1;
$perpage = $datatable['pagination']['perpage'];
$offset = ($page - 1) * $perpage;
// add metadata
$meta = [
'page' => $page,
'perpage' => $perpage,
'pages' => ceil($total / $perpage),
'total' => $total,
'sort' => 'asc',
'field' => 'id'
];
// build query
$query = $qb->select('q');
$this->setQueryFilters($datatable, $query);
// check if sorting is present, otherwise use default
if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) {
$order = $datatable['sort']['sort'] ?? 'asc';
$query->orderBy('q.' . $datatable['sort']['field'], $order);
} else {
$query->orderBy('q.id', 'asc');
}
// get rows for this page
$obj_rows = $query->setFirstResult($offset)
->setMaxResults($perpage)
->getQuery()
->getResult();
// process rows
$rows = [];
foreach ($obj_rows as $orow) {
// add row data
$row['id'] = $orow->getID();
$row['name'] = $orow->getName();
// add row metadata
$row['meta'] = [
'update_url' => '',
'delete_url' => ''
];
// add crud urls
if ($this->isGranted('price_tier.update'))
$row['meta']['update_url'] = $this->generateUrl('price_tier_update_form', ['id' => $row['id']]);
if ($this->isGranted('service_offering.delete'))
$row['meta']['delete_url'] = $this->generateUrl('price_tier_delete', ['id' => $row['id']]);
$rows[] = $row;
}
// response
return $this->json([
'meta' => $meta,
'data' => $rows
]);
}
/**
* @Menu(selected="price_tier.list")
* @IsGranted("price_tier.add")
*/
public function addForm(EntityManagerInterface $em)
{
$pt = new PriceTier();
// get the supported areas
$sets = $this->generateFormSets($em);
$params = [
'obj' => $pt,
'sets' => $sets,
'mode' => 'create',
];
// response
return $this->render('price-tier/form.html.twig', $params);
}
/**
* @IsGranted("price_tier.add")
*/
public function addSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator)
{
// initialize error list
$error_array = [];
$pt = new PriceTier();
$error_array = $this->validateRequest($em, $req);
$this->setObject($pt, $req);
// validate
$errors = $validator->validate($pt);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// validated! save the entity
$em->persist($pt);
// set the price tier id for the selected supported areas
$this->updateSupportedAreas($em, $pt, $req);
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!'
]);
}
/**
* @Menu(selected="price_tier_list")
* @ParamConverter("pt", class="App\Entity\PriceTier")
* @IsGranted("price_tier.update")
*/
public function updateForm($id, EntityManagerInterface $em, PriceTier $pt)
{
// get the supported areas
$sets = $this->generateFormSets($em, $pt);
$params = [
'obj' => $pt,
'sets' => $sets,
'mode' => 'update',
];
// response
return $this->render('price-tier/form.html.twig', $params);
}
/**
* @ParamConverter("pt", class="App\Entity\PriceTier")
* @IsGranted("price_tier.update")
*/
public function updateSubmit(Request $req, EntityManagerInterface $em, ValidatorInterface $validator, PriceTier $pt)
{
// initialize error list
$error_array = [];
// clear supported areas of price tier
$this->clearPriceTierSupportedAreas($em, $pt);
$error_array = $this->validateRequest($em, $req);
$this->setObject($pt, $req);
// validate
$errors = $validator->validate($pt);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// set the price tier id for the selected supported areas
$this->updateSupportedAreas($em, $pt, $req);
// validated! save the entity
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!'
]);
}
/**
* @ParamConverter("pt", class="App\Entity\PriceTier")
* @IsGranted("price_tier.delete")
*/
public function deleteSubmit(EntityManagerInterface $em, PriceTier $pt)
{
// clear supported areas of price tier
$this->clearPriceTierSupportedAreas($em, $pt);
// delete this object
$em->remove($pt);
$em->flush();
// response
$response = new Response();
$response->setStatusCode(Response::HTTP_OK);
$response->send();
}
protected function validateRequest(EntityManagerInterface $em, Request $req)
{
// get areas
$areas = $req->request->get('areas');
// check if no areas selected aka empty
if (!empty($areas))
{
foreach ($areas as $area_id)
{
$supported_area = $em->getRepository(SupportedArea::class)->find($area_id);
if ($supported_area == null)
return ['areas' => 'Invalid area'];
// check if supported area already belongs to a price tier
if ($supported_area->getPriceTier() != null)
return ['areas' => 'Area already belongs to a price tier.'];
}
}
return null;
}
protected function setObject(PriceTier $obj, Request $req)
{
// clear supported areas first
$obj->clearSupportedAreas();
$obj->setName($req->request->get('name'));
}
protected function clearPriceTierSupportedAreas(EntityManagerInterface $em, PriceTier $obj)
{
// find the supported areas set with the price tier
$areas = $em->getRepository(SupportedArea::class)->findBy(['price_tier' => $obj]);
if (!empty($areas))
{
// set the price tier id for the supported areas to null
foreach ($areas as $area)
{
$area->setPriceTier(null);
}
$em->flush();
}
}
protected function updateSupportedAreas(EntityManagerInterface $em, PriceTier $obj, Request $req)
{
// get the selected areas
$areas = $req->request->get('areas');
// check if no areas selected aka empty
if (!empty($areas))
{
foreach ($areas as $area_id)
{
// get supported area
$supported_area = $em->getRepository(SupportedArea::class)->find($area_id);
if ($supported_area != null)
$supported_area->setPriceTier($obj);
}
}
}
protected function generateFormSets(EntityManagerInterface $em, PriceTier $pt = null)
{
// get the supported areas with no price tier id or price tier id is set to the one that is being updated
$areas = $em->getRepository(SupportedArea::class)->findBy(['price_tier' => array(null, $pt)]);
$areas_set = [];
foreach ($areas as $area)
{
$areas_set[$area->getID()] = $area->getName();
}
return [
'areas' => $areas_set
];
}
protected function setQueryFilters($datatable, QueryBuilder $query)
{
if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) {
$query->where('q.name LIKE :filter')
->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%');
}
}
}

View file

@ -236,6 +236,10 @@ class ReportController extends Controller
$reason = $jor->getReason();
$dispatched_by = 'system';
if ($jor->getUser() != null)
$dispatched_by = $jor->getUser()->getFullName();
$res[] = [
$jo->getID(),
$jo->getDateSchedule()->format('m/d/Y H:i'),
@ -244,7 +248,7 @@ class ReportController extends Controller
JORejectionReason::getName($jor->getReason()),
$jor->getContactPerson(),
$jor->getRemarks(),
$jor->getUser()->getFullName(),
!empty($jor->getUser()) ? $jor->getUser()->getFullName() : "",
ServiceType::getName($jo->getServiceType()),
];
}
@ -536,7 +540,7 @@ class ReportController extends Controller
'Mobile Number',
'Landline Number',
'Office Number',
'Fax Number',
'Alternative Phone Number',
'Plate Number',
'Date Mobile App Downloaded',
'Mobile Number Using Mobile App',
@ -706,7 +710,7 @@ class ReportController extends Controller
'Customer Mobile Phone',
'Customer Landline Phone',
'Customer Office Phone',
'Customer Fax Phone',
'Customer Alternative Phone',
'Customer Email Address',
'Customer Notes',
'Customer Has Third Party Privacy Policy?',
@ -813,6 +817,7 @@ class ReportController extends Controller
'Plate Number',
'SKU',
'Serial Number',
'Inventory',
'Invoice/DR No.',
'Existing Battery',
'Rider Name',
@ -1141,7 +1146,7 @@ class ReportController extends Controller
'Mobile Phone',
'Landline',
'Office Phone',
'Fax',
'Alternative Phone',
'Email Address',
'Vehicle Manufacturer',
'Vehicle Make',
@ -1932,6 +1937,9 @@ class ReportController extends Controller
$fac_hub_coord_lat = $fac_hub->getCoordinates()->getLatitude();
}
// get inventory count
$inventory = $jo->getInventoryCount();
// find date and time when JO was assigned a hub
$datetime_hub_assign_jo = '';
$date_hub_assign_jo = '';
@ -2254,6 +2262,7 @@ class ReportController extends Controller
$plate_number,
$sku,
$serial,
$inventory,
$jo->getORName(),
$existing_batt,
$rider_name,

View file

@ -14,6 +14,7 @@ use Doctrine\ORM\EntityManagerInterface;
use Catalyst\MenuBundle\Annotation\Menu;
use App\Entity\JobOrder;
use App\Entity\Rider;
use App\Service\JobOrderHandlerInterface;
@ -22,6 +23,9 @@ use App\Ramcar\JOStatus;
use App\Ramcar\ServiceType;
use App\Ramcar\WillingToWaitContent;
use App\Ramcar\CustomerClassification;
use App\Ramcar\CustomerNotWaitReason;
use DateTime;
class ResqJobOrderController extends Controller
{
@ -39,7 +43,7 @@ class ResqJobOrderController extends Controller
/**
* @IsGranted("jo_resq_proc.list")
*/
public function datatableRows(Request $req, JobOrderHandlerInterface $jo_handler)
public function datatableRows(EntityManagerInterface $em, Request $req, $tier, JobOrderHandlerInterface $jo_handler)
{
// get query builder
$qb = $this->getDoctrine()
@ -52,7 +56,14 @@ class ResqJobOrderController extends Controller
// count total records
$tquery = $qb->select('COUNT(q)');
$this->setQueryFilters($datatable, $tquery, $qb, JOStatus::PENDING, TransactionOrigin::MOBILE_APP);
// right now, tier is either proc or all
if ($tier == 'proc')
$this->setQueryFilters($datatable, $tquery, $qb, $tier);
else
{
// default to all
$this->setQueryFilters($datatable, $tquery, $qb, $tier);
}
$total = $tquery->getQuery()
->getSingleScalarResult();
@ -74,9 +85,17 @@ class ResqJobOrderController extends Controller
];
// build query
$qb = $em->getRepository(JobOrder::class)
->createQueryBuilder('q');
$query = $qb->select('q');
$this->setQueryFilters($datatable, $query, $qb, JOStatus::PENDING, TransactionOrigin::MOBILE_APP);
if ($tier == 'proc')
$this->setQueryFilters($datatable, $query, $qb, $tier);
else
{
// default to all
$this->setQueryFilters($datatable, $query, $qb, $tier);
}
// check if sorting is present, otherwise use default
if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) {
@ -110,7 +129,13 @@ class ResqJobOrderController extends Controller
// check if customer is not willing to wait
$will_not_wait = $orow->getWillWait();
if ($will_not_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// check if reason is emergency or rush
$reason = $orow->getReasonNotWait();
if (($reason == CustomerNotWaitReason::EMERGENCY) ||
($reason == CustomerNotWaitReason::RUSH_REQUEST))
$is_emergency = true;
}
// add row data
$row['id'] = $orow->getID();
@ -125,6 +150,9 @@ class ResqJobOrderController extends Controller
$row['is_mobile'] = $orow->getSource() == TransactionOrigin::MOBILE_APP;
$row['is_vip'] = $is_vip;
$row['is_emergency'] = $is_emergency;
$row['flag_cust_new'] = $orow->isCustNew();
$row['date_assign'] = !empty($orow->getDateAssign()) ? $orow->getDateAssign()->format("c") : null;
$row['date_fulfill'] = !empty($orow->getDateFulfill()) ? $orow->getDateFulfill()->format("c") : null;
$processor = $orow->getProcessedBy();
if ($processor == null)
@ -132,11 +160,28 @@ class ResqJobOrderController extends Controller
else
$row['processor'] = $orow->getProcessedBy()->getFullName();
// get the assigned hub, if any
$assigned_hub = $orow->getHub();
if ($assigned_hub == null)
$row['assigned_hub'] = '';
else
$row['assigned_hub'] = $orow->getHub()->getName();
// add the items for Actions
$jo_id = $orow->getID();
if ($tier == 'proc')
{
$row['meta']['update_url'] = $this->generateUrl('jo_proc_form', ['id' => $jo_id, 'origin' => 'resq']);
$row['meta']['unlock_url'] = $this->generateUrl('jo_proc_unlock', ['id' => $jo_id, 'origin' => 'resq']);
}
else
{
// default to all
$row['meta']['update_url'] = $this->generateUrl('jo_all_form', ['id' => $jo_id, 'origin' => 'resq']);
$row['meta']['pdf_url'] = $this->generateUrl('jo_pdf_form', ['id' => $jo_id, 'origin' => 'resq']);
$row['meta']['view_url'] = $this->generateUrl('jo_all_view_form',['id' => $jo_id, 'origin' => 'resq']);
}
$rows[] = $row;
}
@ -148,11 +193,77 @@ class ResqJobOrderController extends Controller
]);
}
protected function setQueryFilters($datatable, &$query, $qb, $status, $source)
/**
* @Menu(selected="jo_resq_all")
* @IsGranted("jo_resq_all.list")
*/
public function listAll(EntityManagerInterface $em)
{
$params['table_refresh_rate'] = $this->container->getParameter('job_order_refresh_interval');
// get riders for dropdown
$params['riders'] = $em->getRepository(Rider::class)->findAll();
return $this->render('resq-job-order/list.all.html.twig', $params);
}
protected function setQueryFilters($datatable, &$query, $qb, $tier)
{
switch ($tier)
{
case 'proc':
$query->where('q.status = :status')
->andWhere('q.source = :source')
->setParameter('status', $status)
->setParameter('source', $source);
->setParameter('status', JOStatus::PENDING)
->setParameter('source', TransactionOrigin::MOBILE_APP);
if (isset($datatable['query']['data-rows-search']))
{
$query->andWhere('q.plate_number like :filter')
->orWhere('q.phone_mobile like :filter')
->orWhere('q.first_name like :filter')
->orWhere('q.last_name like :filter')
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
}
break;
case 'all':
if (isset($datatable['query']['data-rows-search']))
{
$query->andWhere('q.plate_number like :filter')
->orWhere('q.phone_mobile like :filter')
->orWhere('q.first_name like :filter')
->orWhere('q.last_name like :filter')
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
}
if (isset($datatable['query']['rider']))
{
$query->innerJoin('q.rider', 'r')
->andWhere('r.id = :rider_id')
->setParameter('rider_id', $datatable['query']['rider']);
}
if (isset($datatable['query']['schedule_date']))
{
$start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00';
$end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00';
$date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start);
$date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end);
$query->andWhere('q.date_schedule >= :date_start')
->andWhere('q.date_schedule <= :date_end')
->setParameter('date_start', $date_start)
->setParameter('date_end', $date_end);
}
$query->andWhere('q.source = :source')
->setParameter('source', TransactionOrigin::MOBILE_APP);
break;
default:
$query->where('q.source = :source')
->setParameter('source', TransactionOrigin::MOBILE_APP);
break;
}
}
}

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