@fleetbase/storefront-engine 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/.php-cs-fixer.php +29 -0
  2. package/LICENSE.md +16 -4
  3. package/README.md +106 -13
  4. package/addon/components/modals/add-store-to-category.hbs +5 -0
  5. package/addon/components/modals/create-network-category.hbs +28 -28
  6. package/addon/components/modals/share-network.hbs +3 -3
  7. package/addon/components/modals/store-details.hbs +22 -0
  8. package/addon/components/modals/store-form.hbs +22 -0
  9. package/addon/components/modals/store-form.js +21 -0
  10. package/addon/components/network-category-picker.hbs +40 -0
  11. package/addon/components/network-category-picker.js +83 -0
  12. package/addon/components/store-selector.hbs +4 -2
  13. package/addon/components/widget/orders.js +22 -17
  14. package/addon/controllers/networks/index/network/index.js +133 -9
  15. package/addon/controllers/networks/index/network/stores.js +422 -153
  16. package/addon/controllers/networks/index.js +66 -4
  17. package/addon/controllers/products/index/category.js +1 -1
  18. package/addon/controllers/settings/index.js +14 -7
  19. package/addon/controllers/settings/notifications.js +1 -1
  20. package/addon/models/store.js +44 -7
  21. package/addon/routes/networks/index/network/customers.js +4 -1
  22. package/addon/routes/networks/index/network/orders.js +4 -1
  23. package/addon/routes/networks/index/network/stores.js +19 -15
  24. package/addon/serializers/store.js +1 -0
  25. package/addon/templates/customers/index.hbs +0 -11
  26. package/addon/templates/networks/index/network/index.hbs +1 -1
  27. package/addon/templates/networks/index/network/stores.hbs +8 -163
  28. package/addon/templates/networks/index/network.hbs +2 -3
  29. package/addon/utils/create-shareable-link.js +21 -0
  30. package/app/adapters/addon-category.js +1 -1
  31. package/app/adapters/gateway.js +1 -1
  32. package/app/adapters/network.js +1 -1
  33. package/app/adapters/notification-channel.js +1 -1
  34. package/app/adapters/product-addon-category.js +1 -1
  35. package/app/adapters/product-addon.js +1 -1
  36. package/app/adapters/product-hour.js +1 -1
  37. package/app/adapters/product-store-location.js +1 -1
  38. package/app/adapters/product-variant-option.js +1 -1
  39. package/app/adapters/product-variant.js +1 -1
  40. package/app/adapters/product.js +1 -1
  41. package/app/adapters/store-hour.js +1 -1
  42. package/app/adapters/store-location.js +1 -1
  43. package/app/adapters/store.js +1 -1
  44. package/app/adapters/storefront.js +1 -1
  45. package/app/components/file-record.js +1 -1
  46. package/app/components/modals/add-store-hours.js +1 -1
  47. package/app/components/modals/add-store-to-category.js +1 -0
  48. package/app/components/modals/add-stores-to-network.js +1 -1
  49. package/app/components/modals/assign-driver.js +1 -1
  50. package/app/components/modals/create-first-store.js +1 -1
  51. package/app/components/modals/create-gateway.js +1 -1
  52. package/app/components/modals/create-network-category.js +1 -1
  53. package/app/components/modals/create-network.js +1 -1
  54. package/app/components/modals/create-new-variant.js +1 -1
  55. package/app/components/modals/create-notification-channel.js +1 -1
  56. package/app/components/modals/create-product-category.js +1 -1
  57. package/app/components/modals/create-store.js +1 -1
  58. package/app/components/modals/edit-network.js +1 -1
  59. package/app/components/modals/import-products.js +1 -1
  60. package/app/components/modals/incoming-order.js +1 -1
  61. package/app/components/modals/manage-addons.js +1 -1
  62. package/app/components/modals/order-ready-assign-driver.js +1 -1
  63. package/app/components/modals/select-addon-category.js +1 -1
  64. package/app/components/modals/share-network.js +1 -1
  65. package/app/components/modals/store-details.js +1 -0
  66. package/app/components/modals/store-form.js +1 -0
  67. package/app/components/modals/store-location-form.js +1 -1
  68. package/app/components/network-category-picker.js +1 -0
  69. package/app/components/order-card.js +1 -1
  70. package/app/components/schedule-manager.js +1 -1
  71. package/app/components/settings-container.js +1 -1
  72. package/app/components/store-selector.js +1 -1
  73. package/app/components/widget/customers.js +1 -1
  74. package/app/components/widget/orders.js +1 -1
  75. package/app/components/widget/storefront-metrics.js +1 -1
  76. package/app/controllers/application.js +1 -1
  77. package/app/controllers/customers/index.js +1 -1
  78. package/app/controllers/home.js +1 -1
  79. package/app/controllers/networks/index/network/customers.js +1 -1
  80. package/app/controllers/networks/index/network/index.js +1 -1
  81. package/app/controllers/networks/index/network/orders.js +1 -1
  82. package/app/controllers/networks/index/network/stores.js +1 -1
  83. package/app/controllers/networks/index/network.js +1 -1
  84. package/app/controllers/networks/index.js +1 -1
  85. package/app/controllers/orders/index.js +1 -1
  86. package/app/controllers/products/index/category/edit.js +1 -1
  87. package/app/controllers/products/index/category/new.js +1 -1
  88. package/app/controllers/products/index/category.js +1 -1
  89. package/app/controllers/products/index/index/edit.js +1 -1
  90. package/app/controllers/products/index/index.js +1 -1
  91. package/app/controllers/products/index.js +1 -1
  92. package/app/controllers/settings/api.js +1 -1
  93. package/app/controllers/settings/gateways.js +1 -1
  94. package/app/controllers/settings/index.js +1 -1
  95. package/app/controllers/settings/locations.js +1 -1
  96. package/app/controllers/settings/notifications.js +1 -1
  97. package/app/helpers/get-tip-amount.js +1 -1
  98. package/app/models/addon-category.js +1 -1
  99. package/app/models/gateway.js +1 -1
  100. package/app/models/network.js +1 -1
  101. package/app/models/notification-channel.js +1 -1
  102. package/app/models/product-addon-category.js +1 -1
  103. package/app/models/product-addon.js +1 -1
  104. package/app/models/product-hour.js +1 -1
  105. package/app/models/product-store-location.js +1 -1
  106. package/app/models/product-variant-option.js +1 -1
  107. package/app/models/product-variant.js +1 -1
  108. package/app/models/product.js +1 -1
  109. package/app/models/store-hour.js +1 -1
  110. package/app/models/store-location.js +1 -1
  111. package/app/models/store.js +1 -1
  112. package/app/routes/application.js +1 -1
  113. package/app/routes/customers/index/edit.js +1 -1
  114. package/app/routes/customers/index.js +1 -1
  115. package/app/routes/home.js +1 -1
  116. package/app/routes/networks/index/network/customers.js +1 -1
  117. package/app/routes/networks/index/network/index.js +1 -1
  118. package/app/routes/networks/index/network/orders.js +1 -1
  119. package/app/routes/networks/index/network/stores.js +1 -1
  120. package/app/routes/networks/index/network.js +1 -1
  121. package/app/routes/networks/index.js +1 -1
  122. package/app/routes/orders/index/edit.js +1 -1
  123. package/app/routes/orders/index/new.js +1 -1
  124. package/app/routes/orders/index/view.js +1 -1
  125. package/app/routes/orders/index.js +1 -1
  126. package/app/routes/products/index/category/edit.js +1 -1
  127. package/app/routes/products/index/category/new.js +1 -1
  128. package/app/routes/products/index/category.js +1 -1
  129. package/app/routes/products/index/index/edit.js +1 -1
  130. package/app/routes/products/index/index.js +1 -1
  131. package/app/routes/products/index.js +1 -1
  132. package/app/routes/settings/api.js +1 -1
  133. package/app/routes/settings/gateways.js +1 -1
  134. package/app/routes/settings/index.js +1 -1
  135. package/app/routes/settings/locations.js +1 -1
  136. package/app/routes/settings/notifications.js +1 -1
  137. package/app/serializers/addon-category.js +1 -1
  138. package/app/serializers/network.js +1 -1
  139. package/app/serializers/notification-channel.js +1 -1
  140. package/app/serializers/product-addon-category.js +1 -1
  141. package/app/serializers/product-variant.js +1 -1
  142. package/app/serializers/product.js +1 -1
  143. package/app/serializers/store-location.js +1 -1
  144. package/app/serializers/store.js +1 -1
  145. package/app/services/storefront.js +1 -1
  146. package/app/templates/customers/index/edit.js +1 -1
  147. package/app/templates/customers/index.js +1 -1
  148. package/app/templates/home.js +1 -1
  149. package/app/templates/networks/index/network/customers.js +1 -1
  150. package/app/templates/networks/index/network/index.js +1 -1
  151. package/app/templates/networks/index/network/orders.js +1 -1
  152. package/app/templates/networks/index/network/stores.js +1 -1
  153. package/app/templates/networks/index/network.js +1 -1
  154. package/app/templates/networks/index.js +1 -1
  155. package/app/templates/orders/index/edit.js +1 -1
  156. package/app/templates/orders/index/new.js +1 -1
  157. package/app/templates/orders/index/view.js +1 -1
  158. package/app/templates/orders/index.js +1 -1
  159. package/app/templates/products/index/category/edit.js +1 -1
  160. package/app/templates/products/index/category/new.js +1 -1
  161. package/app/templates/products/index/category.js +1 -1
  162. package/app/templates/products/index/index/edit.js +1 -1
  163. package/app/templates/products/index/index.js +1 -1
  164. package/app/templates/products/index.js +1 -1
  165. package/app/templates/settings/api.js +1 -1
  166. package/app/templates/settings/gateways.js +1 -1
  167. package/app/templates/settings/index.js +1 -1
  168. package/app/templates/settings/locations.js +1 -1
  169. package/app/templates/settings/notifications.js +1 -1
  170. package/app/templates/settings.js +1 -1
  171. package/app/utils/create-shareable-link.js +1 -0
  172. package/app/utils/get-gateway-schemas.js +1 -1
  173. package/app/utils/get-notification-schemas.js +1 -1
  174. package/composer.json +88 -0
  175. package/extension.json +10 -0
  176. package/package.json +6 -7
  177. package/phpstan.neon.dist +8 -0
  178. package/phpunit.xml.dist +16 -0
  179. package/server/.gitattributes +14 -0
  180. package/server/README.md +40 -0
  181. package/server/config/api.php +101 -0
  182. package/server/config/database.connections.php +57 -0
  183. package/server/config/storefront.php +19 -0
  184. package/server/config/twilio-notification-channel.php +36 -0
  185. package/server/migrations/2023_05_03_025307_create_carts_table.php +44 -0
  186. package/server/migrations/2023_05_03_025307_create_checkouts_table.php +51 -0
  187. package/server/migrations/2023_05_03_025307_create_gateways_table.php +48 -0
  188. package/server/migrations/2023_05_03_025307_create_network_stores_table.php +36 -0
  189. package/server/migrations/2023_05_03_025307_create_networks_table.php +56 -0
  190. package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +43 -0
  191. package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +44 -0
  192. package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +38 -0
  193. package/server/migrations/2023_05_03_025307_create_product_addons_table.php +43 -0
  194. package/server/migrations/2023_05_03_025307_create_product_hours_table.php +37 -0
  195. package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +34 -0
  196. package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +40 -0
  197. package/server/migrations/2023_05_03_025307_create_product_variants_table.php +44 -0
  198. package/server/migrations/2023_05_03_025307_create_products_table.php +59 -0
  199. package/server/migrations/2023_05_03_025307_create_reviews_table.php +41 -0
  200. package/server/migrations/2023_05_03_025307_create_store_hours_table.php +37 -0
  201. package/server/migrations/2023_05_03_025307_create_store_locations_table.php +38 -0
  202. package/server/migrations/2023_05_03_025307_create_stores_table.php +57 -0
  203. package/server/migrations/2023_05_03_025307_create_votes_table.php +39 -0
  204. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +40 -0
  205. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +48 -0
  206. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +40 -0
  207. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +40 -0
  208. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +42 -0
  209. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +40 -0
  210. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +40 -0
  211. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +38 -0
  212. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +38 -0
  213. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +32 -0
  214. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +34 -0
  215. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +32 -0
  216. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +32 -0
  217. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +44 -0
  218. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +38 -0
  219. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +32 -0
  220. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +40 -0
  221. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +42 -0
  222. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +38 -0
  223. package/server/src/Auth/Schemas/Storefront.php +95 -0
  224. package/server/src/Console/Commands/NotifyStorefrontOrderNearby.php +86 -0
  225. package/server/src/Expansions/EntityExpansion.php +44 -0
  226. package/server/src/Http/Controllers/ActionController.php +119 -0
  227. package/server/src/Http/Controllers/AddonCategoryController.php +13 -0
  228. package/server/src/Http/Controllers/CustomerController.php +13 -0
  229. package/server/src/Http/Controllers/GatewayController.php +13 -0
  230. package/server/src/Http/Controllers/MetricsController.php +71 -0
  231. package/server/src/Http/Controllers/NetworkController.php +170 -0
  232. package/server/src/Http/Controllers/NotificationChannelController.php +13 -0
  233. package/server/src/Http/Controllers/OrderController.php +154 -0
  234. package/server/src/Http/Controllers/ProductAddonCategoryController.php +14 -0
  235. package/server/src/Http/Controllers/ProductAddonController.php +14 -0
  236. package/server/src/Http/Controllers/ProductController.php +123 -0
  237. package/server/src/Http/Controllers/ProductHourController.php +13 -0
  238. package/server/src/Http/Controllers/ProductVariantController.php +13 -0
  239. package/server/src/Http/Controllers/ProductVariantOptionController.php +13 -0
  240. package/server/src/Http/Controllers/ReviewController.php +13 -0
  241. package/server/src/Http/Controllers/StoreController.php +26 -0
  242. package/server/src/Http/Controllers/StoreHourController.php +13 -0
  243. package/server/src/Http/Controllers/StoreLocationController.php +13 -0
  244. package/server/src/Http/Controllers/StorefrontController.php +15 -0
  245. package/server/src/Http/Controllers/VoteController.php +13 -0
  246. package/server/src/Http/Controllers/v1/CartController.php +161 -0
  247. package/server/src/Http/Controllers/v1/CategoryController.php +138 -0
  248. package/server/src/Http/Controllers/v1/CheckoutController.php +957 -0
  249. package/server/src/Http/Controllers/v1/CustomerController.php +482 -0
  250. package/server/src/Http/Controllers/v1/GatewayControllerController.php +11 -0
  251. package/server/src/Http/Controllers/v1/NetworkController.php +281 -0
  252. package/server/src/Http/Controllers/v1/PaymentMethodController.php +11 -0
  253. package/server/src/Http/Controllers/v1/ProductController.php +94 -0
  254. package/server/src/Http/Controllers/v1/ReviewController.php +270 -0
  255. package/server/src/Http/Controllers/v1/ServiceQuoteController.php +402 -0
  256. package/server/src/Http/Controllers/v1/StoreController.php +176 -0
  257. package/server/src/Http/Filter/AddonCategoryFilter.php +19 -0
  258. package/server/src/Http/Filter/CustomerFilter.php +18 -0
  259. package/server/src/Http/Filter/GatewayFilter.php +13 -0
  260. package/server/src/Http/Filter/NetworkFilter.php +18 -0
  261. package/server/src/Http/Filter/NotificationChannelFilter.php +13 -0
  262. package/server/src/Http/Filter/OrderFilter.php +46 -0
  263. package/server/src/Http/Filter/ProductFilter.php +28 -0
  264. package/server/src/Http/Filter/StoreFilter.php +42 -0
  265. package/server/src/Http/Filter/StoreLocationFilter.php +23 -0
  266. package/server/src/Http/Middleware/SetStorefrontSession.php +130 -0
  267. package/server/src/Http/Requests/AddStoreToNetworkCategory.php +45 -0
  268. package/server/src/Http/Requests/CaptureOrderRequest.php +30 -0
  269. package/server/src/Http/Requests/CreateCustomerRequest.php +44 -0
  270. package/server/src/Http/Requests/CreateReviewRequest.php +34 -0
  271. package/server/src/Http/Requests/GetServiceQuoteFromCart.php +40 -0
  272. package/server/src/Http/Requests/InitializeCheckoutRequest.php +38 -0
  273. package/server/src/Http/Requests/NetworkActionRequest.php +43 -0
  274. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +31 -0
  275. package/server/src/Http/Resources/Cart.php +31 -0
  276. package/server/src/Http/Resources/Category.php +48 -0
  277. package/server/src/Http/Resources/Customer.php +36 -0
  278. package/server/src/Http/Resources/Gateway.php +32 -0
  279. package/server/src/Http/Resources/Media.php +29 -0
  280. package/server/src/Http/Resources/Network.php +48 -0
  281. package/server/src/Http/Resources/Product.php +209 -0
  282. package/server/src/Http/Resources/Review.php +45 -0
  283. package/server/src/Http/Resources/ReviewCustomer.php +60 -0
  284. package/server/src/Http/Resources/Store.php +76 -0
  285. package/server/src/Http/Resources/StoreHour.php +29 -0
  286. package/server/src/Http/Resources/StoreLocation.php +33 -0
  287. package/server/src/Imports/ProductsImport.php +20 -0
  288. package/server/src/Jobs/DownloadProductImageUrl.php +60 -0
  289. package/server/src/Listeners/HandleOrderCompleted.php +31 -0
  290. package/server/src/Listeners/HandleOrderDispatched.php +34 -0
  291. package/server/src/Listeners/HandleOrderDriverAssigned.php +37 -0
  292. package/server/src/Listeners/HandleOrderStarted.php +27 -0
  293. package/server/src/Mail/StorefrontNetworkInvite.php +48 -0
  294. package/server/src/Models/AddonCategory.php +30 -0
  295. package/server/src/Models/Cart.php +691 -0
  296. package/server/src/Models/Checkout.php +166 -0
  297. package/server/src/Models/Customer.php +88 -0
  298. package/server/src/Models/Gateway.php +165 -0
  299. package/server/src/Models/Network.php +300 -0
  300. package/server/src/Models/NetworkStore.php +86 -0
  301. package/server/src/Models/NotificationChannel.php +147 -0
  302. package/server/src/Models/PaymentMethod.php +99 -0
  303. package/server/src/Models/Product.php +315 -0
  304. package/server/src/Models/ProductAddon.php +128 -0
  305. package/server/src/Models/ProductAddonCategory.php +90 -0
  306. package/server/src/Models/ProductHour.php +59 -0
  307. package/server/src/Models/ProductStoreLocation.php +77 -0
  308. package/server/src/Models/ProductVariant.php +125 -0
  309. package/server/src/Models/ProductVariantOption.php +86 -0
  310. package/server/src/Models/Review.php +127 -0
  311. package/server/src/Models/Store.php +478 -0
  312. package/server/src/Models/StoreHour.php +59 -0
  313. package/server/src/Models/StoreLocation.php +126 -0
  314. package/server/src/Models/StorefrontModel.php +22 -0
  315. package/server/src/Models/Vote.php +84 -0
  316. package/server/src/Notifications/StorefrontOrderCanceled.php +196 -0
  317. package/server/src/Notifications/StorefrontOrderCompleted.php +201 -0
  318. package/server/src/Notifications/StorefrontOrderCreated.php +157 -0
  319. package/server/src/Notifications/StorefrontOrderDriverAssigned.php +200 -0
  320. package/server/src/Notifications/StorefrontOrderEnroute.php +199 -0
  321. package/server/src/Notifications/StorefrontOrderNearby.php +201 -0
  322. package/server/src/Notifications/StorefrontOrderPreparing.php +202 -0
  323. package/server/src/Notifications/StorefrontOrderReadyForPickup.php +202 -0
  324. package/server/src/Observers/NetworkObserver.php +40 -0
  325. package/server/src/Observers/ProductObserver.php +118 -0
  326. package/server/src/Providers/EventServiceProvider.php +23 -0
  327. package/server/src/Providers/StorefrontServiceProvider.php +103 -0
  328. package/server/src/Support/Metrics.php +193 -0
  329. package/server/src/Support/OrderConfig.php +13 -0
  330. package/server/src/Support/QPay.php +208 -0
  331. package/server/src/Support/Storefront.php +201 -0
  332. package/server/src/routes.php +180 -0
  333. package/server/tests/Feature.php +5 -0
@@ -0,0 +1,691 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Models;
4
+
5
+ use Exception;
6
+ use Fleetbase\Casts\Json;
7
+ use Fleetbase\Models\Company;
8
+ use Fleetbase\FleetOps\Models\Contact;
9
+ use Fleetbase\FleetOps\Support\Utils;
10
+ use Fleetbase\Traits\Expirable;
11
+ use Fleetbase\Traits\HasUuid;
12
+ Use Fleetbase\Traits\HasApiModelBehavior;
13
+ use Fleetbase\Traits\HasPublicId;
14
+ use Illuminate\Support\Arr;
15
+ use Illuminate\Support\Carbon;
16
+ use Illuminate\Support\Str;
17
+ use stdClass;
18
+
19
+ class Cart extends StorefrontModel
20
+ {
21
+ use HasUuid, HasPublicId, HasApiModelBehavior, Expirable;
22
+
23
+ /**
24
+ * The type of public Id to generate
25
+ *
26
+ * @var string
27
+ */
28
+ protected $publicIdType = 'cart';
29
+
30
+ /**
31
+ * The database table used by the model.
32
+ *
33
+ * @var string
34
+ */
35
+ protected $table = 'carts';
36
+
37
+ /**
38
+ * These attributes that can be queried
39
+ *
40
+ * @var array
41
+ */
42
+ protected $searchableColumns = [];
43
+
44
+ /**
45
+ * The attributes that are mass assignable.
46
+ *
47
+ * @var array
48
+ */
49
+ protected $fillable = ['company_uuid', 'user_uuid', 'checkout_uuid', 'customer_id', 'unique_identifier', 'currency', 'discount_code', 'items', 'events', 'expires_at'];
50
+
51
+ /**
52
+ * The attributes that should be cast to native types.
53
+ *
54
+ * @var array
55
+ */
56
+ protected $casts = [
57
+ 'expires_at' => 'datetime'
58
+ ];
59
+
60
+ /**
61
+ * Dynamic attributes that are appended to object
62
+ *
63
+ * @var array
64
+ */
65
+ protected $appends = ['total_items', 'total_unique_items', 'subtotal'];
66
+
67
+ /**
68
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
69
+ */
70
+ public function company()
71
+ {
72
+ return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(Company::class);
73
+ }
74
+
75
+ /**
76
+ *
77
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
78
+ */
79
+ public function user()
80
+ {
81
+ return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(User::class);
82
+ }
83
+
84
+ /**
85
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
86
+ */
87
+ public function customer()
88
+ {
89
+ return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(Contact::class, 'public_id');
90
+ }
91
+
92
+ /**
93
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
94
+ */
95
+ public function checkout()
96
+ {
97
+ return $this->belongsTo(Checkout::class);
98
+ }
99
+
100
+ /**
101
+ * Set cart items.
102
+ *
103
+ * @return void
104
+ */
105
+ public function setItemsAttribute($items)
106
+ {
107
+ $this->attributes['items'] = json_encode($items);
108
+ }
109
+
110
+ /**
111
+ * Set cart events.
112
+ *
113
+ * @return void
114
+ */
115
+ public function setEventsAttribute($events)
116
+ {
117
+ $this->attributes['items'] = json_encode($events);
118
+ }
119
+
120
+ /**
121
+ * Get cart items.
122
+ *
123
+ * @return array
124
+ */
125
+ public function getItemsAttribute($items)
126
+ {
127
+ if (is_array($items)) {
128
+ return $items;
129
+ }
130
+
131
+ return (array) json_decode($items, false);
132
+ }
133
+
134
+ /**
135
+ * Get cart events.
136
+ *
137
+ * @return array
138
+ */
139
+ public function getEventsAttribute($events)
140
+ {
141
+ if (is_array($events)) {
142
+ return $events;
143
+ }
144
+
145
+ return (array) json_decode($events, false);
146
+ }
147
+
148
+ /**
149
+ * Computes subtotal of cart.
150
+ *
151
+ * @return int
152
+ */
153
+ public function getSubtotalAttribute()
154
+ {
155
+ $items = $this->getAttribute('items') ?? [];
156
+ $subtotal = 0;
157
+
158
+ foreach ($items as $item) {
159
+ $subtotal += Utils::numbersOnly($item->subtotal);
160
+ }
161
+
162
+ return $subtotal;
163
+ }
164
+
165
+ /**
166
+ * Computes total items in cart.
167
+ *
168
+ * @return int
169
+ */
170
+ public function getTotalItemsAttribute()
171
+ {
172
+ $items = $this->getAttribute('items') ?? [];
173
+ $total = 0;
174
+
175
+ foreach ($items as $item) {
176
+ $total += Utils::numbersOnly($item->quantity);
177
+ }
178
+
179
+ return $total;
180
+ }
181
+
182
+ /**
183
+ * Computes total unique items in cart.
184
+ *
185
+ * @return int
186
+ */
187
+ public function getTotalUniqueItemsAttribute()
188
+ {
189
+ return count($this->getAttribute('items'));
190
+ }
191
+
192
+ /**
193
+ * The last cart event.
194
+ *
195
+ * @return stdClass|null
196
+ */
197
+ public function getLastEventAttribute()
198
+ {
199
+ $events = $this->getAttribute('events') ?? [];
200
+
201
+ return Arr::last($events);
202
+ }
203
+
204
+ /**
205
+ * If the cart is a multi cart
206
+ *
207
+ * @return boolean
208
+ */
209
+ public function getIsMultiCartAttribute()
210
+ {
211
+ return collect($this->items)->pluck('store_id')->unique()->count() > 1;
212
+ }
213
+
214
+ /**
215
+ * Returns the checkout store id
216
+ *
217
+ * @return string
218
+ */
219
+ public function getCheckoutStoreIdAttribute()
220
+ {
221
+ return collect($this->items)->pluck('store_id')->unique()->first();
222
+ }
223
+
224
+ /**
225
+ * Returns the checkout store ids for all stores being checked out from
226
+ *
227
+ * @return array
228
+ */
229
+ public function getCheckoutStoreIdsAttribute()
230
+ {
231
+ return collect($this->items)->pluck('store_id')->unique()->toArray();
232
+ }
233
+
234
+ /**
235
+ * Returns cart items for a specific store only
236
+ *
237
+ * @return array
238
+ */
239
+ public function getItemsForStore($id)
240
+ {
241
+ if ($id instanceof Store) {
242
+ $id = $id->public_id;
243
+ }
244
+
245
+ return collect($this->items)->filter(function ($cartItem) use ($id) {
246
+ return $cartItem->store_id === $id;
247
+ })->toArray();
248
+ }
249
+
250
+ /**
251
+ * Computes subtotal of specific cart items of a store.
252
+ *
253
+ * @return int
254
+ */
255
+ public function getSubtotalForStore($id)
256
+ {
257
+ $items = $this->getItemsForStore($id);
258
+ $subtotal = 0;
259
+
260
+ foreach ($items as $item) {
261
+ $subtotal += Utils::numbersOnly($item->subtotal);
262
+ }
263
+
264
+ return $subtotal;
265
+ }
266
+
267
+ /**
268
+ * Adds item to cart.
269
+ *
270
+ * @param Product|string $product
271
+ * @param integer $quantity
272
+ * @param array $variants
273
+ * @param array $addons
274
+ * @param string|null $createdAt
275
+ * @return stdClass
276
+ * @throws Exception
277
+ */
278
+ public function add($product, $quantity = 1, $variants = [], $addons = [], $storeLocationId = null, $scheduledAt = null, $createdAt = null)
279
+ {
280
+ if ($product instanceof Product) {
281
+ return $this->addItem($product, $quantity, $variants, $addons, $storeLocationId, $scheduledAt, $createdAt);
282
+ }
283
+
284
+ if (is_string($product)) {
285
+ $product = static::findProduct($product);
286
+
287
+ return $this->add($product, $quantity, $variants, $addons, $storeLocationId, $scheduledAt, $createdAt);
288
+ }
289
+
290
+ throw new Exception('Invalid product provided to cart!');
291
+ }
292
+
293
+ /**
294
+ * Adds an item to cart.
295
+ *
296
+ * @param \Fleetbase\Models\Storefront\Product $product
297
+ * @param integer $quantity
298
+ * @param array $variants
299
+ * @param array $addons
300
+ * @param string $createdAt
301
+ */
302
+ public function addItem(Product $product, $quantity = 1, $variants = [], $addons = [], $storeLocationId = null, $scheduledAt = null, $createdAt = null)
303
+ {
304
+ $id = Utils::generatePublicId('cart_item');
305
+ $cartItem = new stdClass();
306
+
307
+ // set base price
308
+ $price = Utils::numbersOnly($product->is_on_sale ? $product->sale_price : $product->price);
309
+
310
+ // calculate subtotal
311
+ $subtotal = static::calculateProductSubtotal($product, $quantity, $variants, $addons);
312
+
313
+ // if no store location id, default to first store location
314
+ if (empty($storeLocationId)) {
315
+ $storeLocationId = Utils::get($product, 'store.locations.0.public_id');
316
+ }
317
+
318
+ $properties = [
319
+ 'id' => $id,
320
+ 'store_id' => $product->store_id,
321
+ 'store_location_id' => $storeLocationId,
322
+ 'product_id' => $product->public_id,
323
+ 'product_image_url' => $product->primary_image_url,
324
+ 'name' => $product->name,
325
+ 'description' => $product->description,
326
+ 'scheduled_at' => $scheduledAt,
327
+ 'created_at' => $createdAt ?? time(),
328
+ 'updated_at' => time(),
329
+ 'quantity' => $quantity,
330
+ 'price' => $price,
331
+ 'subtotal' => $subtotal,
332
+ 'variants' => $variants,
333
+ 'addons' => $addons
334
+ ];
335
+
336
+ foreach ($properties as $prop => $value) {
337
+ $cartItem->{$prop} = $value;
338
+ }
339
+
340
+ $items = $this->getAttribute('items');
341
+
342
+ $items[] = $cartItem;
343
+
344
+ $this->createEvent('cart.item_added', $cartItem->id, false);
345
+ $this->attributes['items'] = $items;
346
+ $this->save();
347
+
348
+ return $cartItem;
349
+ }
350
+
351
+ /**
352
+ * Adds item to cart.
353
+ *
354
+ * @param stdClass|string $cartItem
355
+ * @param integer $quantity
356
+ * @param array $variants
357
+ * @param array $addons
358
+ * @param string|null $createdAt
359
+ * @return stdClass
360
+ * @throws Exception
361
+ */
362
+ public function updateItem($cartItem, $quantity = 1, $variants = [], $addons = [], $scheduledAt = null)
363
+ {
364
+ if ($cartItem instanceof stdClass) {
365
+ return $this->updateCartItem($cartItem, $quantity, $variants, $addons, $scheduledAt);
366
+ }
367
+
368
+ if (is_string($cartItem)) {
369
+ $cartItem = $this->findCartItem($cartItem);
370
+
371
+ return $this->updateItem($cartItem, $quantity, $variants, $addons, $scheduledAt);
372
+ }
373
+
374
+ throw new Exception('Invalid cart item provided to cart!');
375
+ }
376
+
377
+ /**
378
+ * Updates an item in cart.
379
+ *
380
+ * @param [type] $cartItem
381
+ * @param integer $quantity
382
+ * @param array $variants
383
+ * @param array $addons
384
+ * @return stdClass
385
+ */
386
+ public function updateCartItem($cartItem, $quantity = 1, $variants = [], $addons = [], $scheduledAt = null)
387
+ {
388
+ // get the line item product
389
+ $productId = $cartItem->product_id;
390
+ $product = static::findProduct($productId);
391
+
392
+ // set base price
393
+ $price = Utils::numbersOnly($product->is_on_sale ? $product->sale_price : $product->price);
394
+
395
+ // calculate subtotal
396
+ $subtotal = static::calculateProductSubtotal($product, $quantity, $variants, $addons);
397
+
398
+ // get cart items
399
+ $items = $this->getAttribute('items');
400
+
401
+ // find the item from cart
402
+ $index = $this->findCartItemIndex($cartItem->id);
403
+
404
+ $existingCartItem = $items[$index] ?? new stdClass();
405
+
406
+ $properties = [
407
+ 'id' => $cartItem->id,
408
+ 'store_id' => $product->store_id,
409
+ 'product_id' => $product->public_id,
410
+ 'product_image_url' => $product->primary_image_url,
411
+ 'name' => $product->name,
412
+ 'description' => $product->description,
413
+ 'scheduled_at' => $scheduledAt,
414
+ 'created_at' => $cartItem->created_at,
415
+ 'updated_at' => time(),
416
+ 'quantity' => $quantity ?? $cartItem->quantity,
417
+ 'price' => $price,
418
+ 'subtotal' => $subtotal,
419
+ 'variants' => $variants ?? $cartItem->variants,
420
+ 'addons' => $addons ?? $cartItem->addons
421
+ ];
422
+
423
+ foreach ($properties as $prop => $value) {
424
+ $existingCartItem->{$prop} = $value;
425
+ }
426
+
427
+ $items[$index] = $existingCartItem;
428
+
429
+ $this->createEvent('cart.item_updated', $cartItem->id, false);
430
+ $this->attributes['items'] = $items;
431
+ $this->save();
432
+
433
+ return $existingCartItem;
434
+ }
435
+
436
+ /**
437
+ * Updates a cart item by id.
438
+ *
439
+ * @param string $id
440
+ * @param integer $quantity
441
+ * @param array $variants
442
+ * @param array $addons
443
+ * @return stdClass
444
+ */
445
+ public function updateCartItemById(string $id, $quantity = 1, $variants = [], $addons = [], $scheduledAt = null)
446
+ {
447
+ $cartItem = $this->findCartItem($id);
448
+
449
+ return $this->updateCartItem($cartItem, $quantity, $variants, $addons, $scheduledAt);
450
+ }
451
+
452
+ /**
453
+ * Remove item from cart.
454
+ *
455
+ * @param stdClass|string $cartItem
456
+ * @return stdClass
457
+ * @throws Exception
458
+ */
459
+ public function remove($cartItem)
460
+ {
461
+ if ($cartItem instanceof stdClass) {
462
+ return $this->removeItem($cartItem);
463
+ }
464
+
465
+ if (is_string($cartItem)) {
466
+ $cartItem = $this->findCartItem($cartItem);
467
+
468
+ return $this->remove($cartItem);
469
+ }
470
+
471
+ throw new Exception('Invalid cart item provided to cart!');
472
+ }
473
+
474
+ /**
475
+ * Removes an item from cart.
476
+ *
477
+ * @param stdClass $cartItem
478
+ * @return \Fleetbase\Models\Storefront\Cart
479
+ */
480
+ public function removeItem($cartItem)
481
+ {
482
+ $items = $this->getAttribute('items');
483
+ $index = $this->findCartItemIndex($cartItem->id);
484
+
485
+ unset($items[$index]);
486
+
487
+ $this->createEvent('cart.item_removed', $cartItem->id, false);
488
+ $this->attributes['items'] = $items;
489
+ $this->save();
490
+
491
+ return $this;
492
+ }
493
+
494
+ /**
495
+ * Removes a cart item by id.
496
+ *
497
+ * @param string $id
498
+ * @return \Fleetbase\Models\Storefront\Cart
499
+ */
500
+ public function removeItemById(string $id)
501
+ {
502
+ $cartItem = $this->findCartItem($id);
503
+
504
+ return $this->removeItem($cartItem);
505
+ }
506
+
507
+ /**
508
+ * Empties the cart.
509
+ *
510
+ * @return \Fleetbase\Models\Storefront\Cart
511
+ */
512
+ public function empty()
513
+ {
514
+ $this->createEvent('cart.emptied', null, false);
515
+ $this->attributes['items'] = [];
516
+ $this->updateCurrency(null, false);
517
+ $this->save();
518
+
519
+ return $this;
520
+ }
521
+
522
+ /**
523
+ * Finds an item in cart by id.
524
+ *
525
+ * @param string $id
526
+ * @return stdClass|null
527
+ */
528
+ public function findCartItem(string $id): ?stdClass
529
+ {
530
+ $items = $this->getAttribute('items');
531
+
532
+ $foundCartItem = collect($items)->first(function ($item) use ($id) {
533
+ return $item->id === $id;
534
+ });
535
+
536
+ return $foundCartItem;
537
+ }
538
+
539
+ /**
540
+ * Finds index of a cart item.
541
+ *
542
+ * @param string $id
543
+ * @return integer|null
544
+ */
545
+ public function findCartItemIndex(string $id): ?int
546
+ {
547
+ $items = $this->getAttribute('items');
548
+
549
+ foreach ($items as $index => $item) {
550
+ if ($item->id === $id) {
551
+ return $index;
552
+ }
553
+ }
554
+
555
+ return -1;
556
+ }
557
+
558
+ /**
559
+ * Create a new cart event.
560
+ *
561
+ * @param string $eventName
562
+ * @param string|null $cartItemId
563
+ * @return \Fleetbase\Models\Storefront\Cart
564
+ */
565
+ public function createEvent(string $eventName, $cartItemId = null, $save = true)
566
+ {
567
+ $events = $this->getAttribute('events') ?? [];
568
+
569
+ $events[] = Utils::createObject([
570
+ 'event' => $eventName,
571
+ 'cart_item_id' => $cartItemId,
572
+ 'time' => time()
573
+ ]);
574
+
575
+ $this->attributes['events'] = $events;
576
+
577
+ if ($save) {
578
+ $this->save();
579
+ }
580
+
581
+ return $this;
582
+ }
583
+
584
+ /**
585
+ * Update the cart session currency code.
586
+ *
587
+ * @param string $currencyCode
588
+ * @param boolean $save
589
+ * @return \Fleetbase\Models\Storefront\Cart
590
+ */
591
+ public function updateCurrency(?string $currencyCode = null, $save = false)
592
+ {
593
+ $this->attributes['currency'] = $currencyCode ?? session('storefront_currency');
594
+
595
+ if ($save) {
596
+ $this->save();
597
+ }
598
+
599
+ return $this;
600
+ }
601
+
602
+ /**
603
+ * Reset the cart currency code to the current session.
604
+ *
605
+ * @return \Fleetbase\Models\Storefront\Cart
606
+ */
607
+ public function resetCurrency()
608
+ {
609
+ return $this->updateCurrency(null, true);
610
+ }
611
+
612
+ /**
613
+ * Creates a new cart.
614
+ *
615
+ * @param string|null $uniqueId
616
+ * @return \Fleetbase\Models\Storefront\Cart
617
+ */
618
+ public static function newCart($uniqueId = null): Cart
619
+ {
620
+ return Cart::create([
621
+ 'unique_identifier' => $uniqueId,
622
+ 'company_uuid' => session('company'),
623
+ 'expires_at' => Carbon::now()->addDays(7),
624
+ 'currency' => session('storefront_currency'),
625
+ 'customer_id' => session('customer_id'),
626
+ 'items' => [],
627
+ 'events' => []
628
+ ]);
629
+ }
630
+
631
+ /**
632
+ * Retrieve a cart by id or unique id.
633
+ *
634
+ * @param string $id
635
+ * @return Cart
636
+ */
637
+ public static function retrieve(string $id, bool $excludeCheckedout = true): Cart
638
+ {
639
+ $query = static::where(function ($q) use ($id) {
640
+ $q->where('public_id', $id);
641
+ $q->orWhere('unique_identifier', $id);
642
+ });
643
+
644
+ if ($excludeCheckedout) {
645
+ $query->whereNull('checkout_uuid');
646
+ }
647
+
648
+ $cart = $query->first();
649
+
650
+ if (!$cart) {
651
+ return static::newCart(!Str::startsWith($id, 'cart_') ? $id : null);
652
+ }
653
+
654
+ return $cart;
655
+ }
656
+
657
+ /**
658
+ * Calculates the subtotal for a product using quantity vairants and addons.
659
+ *
660
+ * @param \Fleetbase\Models\Storefront\Product $product
661
+ * @param integer $quantity
662
+ * @param array $variants
663
+ * @param array $addons
664
+ * @return integer
665
+ */
666
+ public static function calculateProductSubtotal(Product $product, $quantity = 1, $variants = [], $addons = []): int
667
+ {
668
+ $subtotal = $product->is_on_sale ? $product->sale_price : $product->price;
669
+
670
+ foreach ($variants as $variant) {
671
+ $subtotal += Utils::get($variant, 'additional_cost');
672
+ }
673
+
674
+ foreach ($addons as $addon) {
675
+ $subtotal += Utils::get($addon, 'is_on_sale') ? Utils::get($addon, 'sale_price') : Utils::get($addon, 'price');
676
+ }
677
+
678
+ return $subtotal * $quantity;
679
+ }
680
+
681
+ /**
682
+ * Finds a product via id.
683
+ *
684
+ * @param string $id
685
+ * @return Product|null
686
+ */
687
+ public static function findProduct(string $id): ?Product
688
+ {
689
+ return Product::select(['uuid', 'store_uuid', 'public_id', 'name', 'description', 'price', 'sale_price', 'is_on_sale'])->where(['public_id' => $id])->with([])->first();
690
+ }
691
+ }