From 3e9432e89e4c669c1fcdf3f32d7e8f6c4821a6a7 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 12 Jan 2026 15:05:22 +0000 Subject: [PATCH] v1 --- .gitignore | 5 +- 502.html | 4 +- README.md | 2 +- backend/.env | 6 +- backend/README.md | 6 +- backend/package.json | 4 +- backend/src/config.js | 12 +- backend/src/db/api/categories.js | 3 + backend/src/db/api/customers.js | 70 +- backend/src/db/api/discounts.js | 519 +++ .../{inventory.js => inventory_movements.js} | 212 +- backend/src/db/api/order_items.js | 102 +- backend/src/db/api/orders.js | 503 ++- backend/src/db/api/payments.js | 168 +- backend/src/db/api/permissions.js | 3 + backend/src/db/api/products.js | 171 +- backend/src/db/api/returns.js | 552 +++ .../api/{product_variants.js => reviews.js} | 244 +- backend/src/db/api/roles.js | 3 + backend/src/db/api/shipments.js | 97 +- backend/src/db/api/suppliers.js | 41 +- backend/src/db/api/tags.js | 366 ++ backend/src/db/api/users.js | 7 + backend/src/db/db.config.js | 2 +- .../{1768218607130.js => 1768230063509.js} | 1407 +++++--- backend/src/db/models/categories.js | 3 + backend/src/db/models/customers.js | 14 +- backend/src/db/models/discounts.js | 135 + .../{inventory.js => inventory_movements.js} | 51 +- backend/src/db/models/order_items.js | 26 +- backend/src/db/models/orders.js | 169 +- backend/src/db/models/payments.js | 62 +- backend/src/db/models/permissions.js | 3 + backend/src/db/models/products.js | 65 +- backend/src/db/models/returns.js | 139 + .../{product_variants.js => reviews.js} | 76 +- backend/src/db/models/roles.js | 3 + backend/src/db/models/shipments.js | 21 +- backend/src/db/models/suppliers.js | 11 +- backend/src/db/models/tags.js | 88 + backend/src/db/models/users.js | 11 + .../db/seeders/20200430130760-user-roles.js | 574 +++- .../db/seeders/20231127130745-sample-data.js | 3016 ++++++++++++----- backend/src/index.js | 30 +- backend/src/routes/categories.js | 8 +- backend/src/routes/customers.js | 25 +- backend/src/routes/dashboard.js | 60 + .../{product_variants.js => discounts.js} | 122 +- backend/src/routes/inventory_movements.js | 433 +++ backend/src/routes/order_items.js | 21 +- backend/src/routes/orders.js | 28 +- backend/src/routes/payments.js | 17 +- backend/src/routes/products.js | 27 +- backend/src/routes/returns.js | 436 +++ backend/src/routes/reviews.js | 435 +++ backend/src/routes/shipments.js | 14 +- backend/src/routes/suppliers.js | 20 +- backend/src/routes/{inventory.js => tags.js} | 114 +- .../services/{inventory.js => discounts.js} | 22 +- backend/src/services/inventory_movements.js | 138 + backend/src/services/notifications/list.js | 2 +- .../{product_variants.js => returns.js} | 22 +- backend/src/services/reviews.js | 138 + backend/src/services/search.js | 273 +- backend/src/services/tags.js | 138 + docker/docker-compose.yml | 2 +- frontend/README.md | 2 +- frontend/src/colors.ts | 30 +- frontend/src/components/AsideMenuLayer.tsx | 2 +- .../components/Categories/CardCategories.tsx | 36 + .../components/Categories/ListCategories.tsx | 26 +- .../components/Categories/TableCategories.tsx | 8 +- .../Categories/configureCategoriesCols.tsx | 52 + .../components/Customers/CardCustomers.tsx | 84 + .../components/Customers/ListCustomers.tsx | 58 +- .../components/Customers/TableCustomers.tsx | 19 +- .../Customers/configureCustomersCols.tsx | 106 + .../components/Discounts/CardDiscounts.tsx | 183 + .../components/Discounts/ListDiscounts.tsx | 136 + .../TableDiscounts.tsx} | 24 +- .../Discounts/configureDiscountsCols.tsx | 181 + frontend/src/components/FormField.tsx | 4 +- frontend/src/components/FormImagePicker.tsx | 2 +- frontend/src/components/ImageField.tsx | 2 +- .../components/Inventory/CardInventory.tsx | 99 - .../CardInventory_movements.tsx | 159 + .../ListInventory_movements.tsx | 120 + .../TableInventory_movements.tsx | 476 +++ .../configureInventory_movementsCols.tsx | 154 + .../src/components/KanbanBoard/KanbanCard.tsx | 2 +- .../components/KanbanBoard/KanbanColumn.tsx | 2 +- .../src/components/ListActionsPopover.tsx | 2 +- frontend/src/components/LoadingSpinner.tsx | 2 +- frontend/src/components/NavBar.tsx | 2 +- frontend/src/components/NavBarItem.tsx | 2 +- .../Order_items/CardOrder_items.tsx | 74 +- .../Order_items/ListOrder_items.tsx | 50 +- .../Order_items/TableOrder_items.tsx | 4 +- .../Order_items/configureOrder_itemsCols.tsx | 100 + frontend/src/components/Orders/CardOrders.tsx | 180 + frontend/src/components/Orders/ListOrders.tsx | 122 +- .../src/components/Orders/TableOrders.tsx | 74 +- .../components/Orders/configureOrdersCols.tsx | 264 ++ .../src/components/Payments/CardPayments.tsx | 105 +- .../src/components/Payments/ListPayments.tsx | 73 +- .../src/components/Payments/TablePayments.tsx | 19 +- .../Payments/configurePaymentsCols.tsx | 136 + .../Permissions/ListPermissions.tsx | 2 +- .../Permissions/TablePermissions.tsx | 4 +- .../Product_variants/ListProduct_variants.tsx | 80 - .../configureProduct_variantsCols.tsx | 68 - .../src/components/Products/CardProducts.tsx | 165 +- .../src/components/Products/ListProducts.tsx | 109 +- .../src/components/Products/TableProducts.tsx | 19 +- .../Products/configureProductsCols.tsx | 209 ++ .../src/components/Returns/CardReturns.tsx | 183 + .../src/components/Returns/ListReturns.tsx | 136 + .../src/components/Returns/TableReturns.tsx | 489 +++ .../Returns/configureReturnsCols.tsx | 187 + .../src/components/Reviews/CardReviews.tsx | 171 + .../src/components/Reviews/ListReviews.tsx | 128 + .../TableReviews.tsx} | 32 +- .../Reviews/configureReviewsCols.tsx | 170 + frontend/src/components/Roles/ListRoles.tsx | 2 +- frontend/src/components/Roles/TableRoles.tsx | 4 +- .../components/Shipments/CardShipments.tsx | 72 + .../components/Shipments/ListShipments.tsx | 50 +- .../components/Shipments/TableShipments.tsx | 4 +- .../Shipments/configureShipmentsCols.tsx | 96 + .../components/Suppliers/CardSuppliers.tsx | 72 + .../components/Suppliers/ListSuppliers.tsx | 50 +- .../components/Suppliers/TableSuppliers.tsx | 19 +- .../Suppliers/configureSuppliersCols.tsx | 90 + frontend/src/components/SwitchField.tsx | 9 +- .../CardTags.tsx} | 46 +- .../ListInventory.tsx => Tags/ListTags.tsx} | 36 +- frontend/src/components/Tags/TableTags.tsx | 476 +++ .../configureTagsCols.tsx} | 36 +- frontend/src/components/Users/ListUsers.tsx | 2 +- frontend/src/components/Users/TableUsers.tsx | 4 +- frontend/src/css/_calendar.css | 39 +- frontend/src/css/_checkbox-radio-switch.css | 4 +- frontend/src/css/_rich-text.css | 58 - frontend/src/css/_select-dropdown.css | 16 +- frontend/src/css/_table.css | 26 +- frontend/src/css/main.css | 35 +- frontend/src/helpers/dataFormatter.js | 146 +- frontend/src/menuAside.ts | 64 +- frontend/src/pages/_app.tsx | 6 +- .../src/pages/categories/[categoriesId].tsx | 229 ++ .../src/pages/categories/categories-edit.tsx | 229 ++ .../src/pages/categories/categories-list.tsx | 6 +- .../src/pages/categories/categories-new.tsx | 143 + .../src/pages/categories/categories-table.tsx | 8 +- .../src/pages/categories/categories-view.tsx | 224 ++ .../src/pages/customers/[customersId].tsx | 446 +++ .../src/pages/customers/customers-edit.tsx | 446 +++ .../src/pages/customers/customers-list.tsx | 8 +- .../src/pages/customers/customers-new.tsx | 348 ++ .../src/pages/customers/customers-table.tsx | 6 +- .../src/pages/customers/customers-view.tsx | 345 ++ frontend/src/pages/dashboard.tsx | 630 +--- .../src/pages/discounts/[discountsId].tsx | 585 ++++ .../src/pages/discounts/discounts-edit.tsx | 582 ++++ .../discounts-list.tsx} | 38 +- .../src/pages/discounts/discounts-new.tsx | 448 +++ .../src/pages/discounts/discounts-table.tsx | 164 + .../src/pages/discounts/discounts-view.tsx | 343 ++ frontend/src/pages/index.tsx | 4 +- .../src/pages/inventory/[inventoryId].tsx | 122 - .../src/pages/inventory/inventory-edit.tsx | 119 - .../[inventory_movementsId].tsx | 493 +++ .../inventory_movements-edit.tsx | 490 +++ .../inventory_movements-list.tsx | 170 + .../inventory_movements-new.tsx | 345 ++ .../inventory_movements-table.tsx | 168 + .../inventory_movements-view.tsx | 304 ++ frontend/src/pages/login.tsx | 12 +- .../src/pages/order_items/[order_itemsId].tsx | 432 +++ .../pages/order_items/order_items-edit.tsx | 432 +++ .../pages/order_items/order_items-list.tsx | 10 +- .../src/pages/order_items/order_items-new.tsx | 304 ++ .../pages/order_items/order_items-table.tsx | 10 +- .../pages/order_items/order_items-view.tsx | 231 ++ frontend/src/pages/orders/[ordersId].tsx | 1176 +++++++ frontend/src/pages/orders/orders-edit.tsx | 1176 +++++++ frontend/src/pages/orders/orders-list.tsx | 22 +- frontend/src/pages/orders/orders-new.tsx | 776 ++++- frontend/src/pages/orders/orders-table.tsx | 20 +- frontend/src/pages/orders/orders-view.tsx | 993 +++++- frontend/src/pages/payments/[paymentsId].tsx | 543 +++ frontend/src/pages/payments/payments-edit.tsx | 543 +++ frontend/src/pages/payments/payments-list.tsx | 12 +- frontend/src/pages/payments/payments-new.tsx | 428 +++ .../src/pages/payments/payments-table.tsx | 10 +- frontend/src/pages/payments/payments-view.tsx | 279 ++ .../pages/permissions/permissions-view.tsx | 3 + frontend/src/pages/privacy-policy.tsx | 2 +- .../product_variants-view.tsx | 152 - frontend/src/pages/products/[productsId].tsx | 902 +++++ frontend/src/pages/products/products-edit.tsx | 902 +++++ frontend/src/pages/products/products-list.tsx | 20 +- frontend/src/pages/products/products-new.tsx | 611 ++++ .../src/pages/products/products-table.tsx | 18 +- frontend/src/pages/products/products-view.tsx | 781 ++++- frontend/src/pages/returns/[returnsId].tsx | 629 ++++ frontend/src/pages/returns/returns-edit.tsx | 626 ++++ frontend/src/pages/returns/returns-list.tsx | 170 + frontend/src/pages/returns/returns-new.tsx | 458 +++ frontend/src/pages/returns/returns-table.tsx | 168 + frontend/src/pages/returns/returns-view.tsx | 376 ++ frontend/src/pages/reviews/[reviewsId].tsx | 552 +++ frontend/src/pages/reviews/reviews-edit.tsx | 549 +++ frontend/src/pages/reviews/reviews-list.tsx | 170 + .../reviews-new.tsx} | 311 +- .../reviews-table.tsx} | 44 +- frontend/src/pages/reviews/reviews-view.tsx | 339 ++ frontend/src/pages/roles/[rolesId].tsx | 6 + frontend/src/pages/roles/roles-edit.tsx | 6 + frontend/src/pages/roles/roles-view.tsx | 9 + .../src/pages/shipments/[shipmentsId].tsx | 402 +++ .../src/pages/shipments/shipments-edit.tsx | 402 +++ .../src/pages/shipments/shipments-list.tsx | 6 +- .../src/pages/shipments/shipments-new.tsx | 309 ++ .../src/pages/shipments/shipments-table.tsx | 6 +- .../src/pages/shipments/shipments-view.tsx | 210 ++ .../src/pages/suppliers/[suppliersId].tsx | 380 +++ .../src/pages/suppliers/suppliers-edit.tsx | 380 +++ .../src/pages/suppliers/suppliers-list.tsx | 6 +- .../src/pages/suppliers/suppliers-new.tsx | 296 ++ .../src/pages/suppliers/suppliers-table.tsx | 4 +- .../src/pages/suppliers/suppliers-view.tsx | 293 +- .../[tagsId].tsx} | 170 +- .../tags-edit.tsx} | 162 +- .../inventory-list.tsx => tags/tags-list.tsx} | 34 +- .../tags-new.tsx} | 116 +- .../tags-table.tsx} | 34 +- .../inventory-view.tsx => tags/tags-view.tsx} | 87 +- frontend/src/pages/terms-of-use.tsx | 2 +- frontend/src/pages/users/[usersId].tsx | 12 + frontend/src/pages/users/users-edit.tsx | 12 + frontend/src/pages/users/users-view.tsx | 162 + frontend/src/stores/dashboardSlice.ts | 50 + .../discountsSlice.ts} | 50 +- .../inventory_movementsSlice.ts | 231 ++ .../returnsSlice.ts} | 50 +- frontend/src/stores/reviews/reviewsSlice.ts | 231 ++ frontend/src/stores/store.ts | 20 +- frontend/src/stores/styleSlice.ts | 42 +- frontend/src/stores/tags/tagsSlice.ts | 231 ++ frontend/src/styles.ts | 27 - frontend/tailwind.config.js | 26 - 252 files changed, 41165 insertions(+), 4225 deletions(-) create mode 100644 backend/src/db/api/discounts.js rename backend/src/db/api/{inventory.js => inventory_movements.js} (69%) create mode 100644 backend/src/db/api/returns.js rename backend/src/db/api/{product_variants.js => reviews.js} (59%) create mode 100644 backend/src/db/api/tags.js rename backend/src/db/migrations/{1768218607130.js => 1768230063509.js} (77%) create mode 100644 backend/src/db/models/discounts.js rename backend/src/db/models/{inventory.js => inventory_movements.js} (70%) create mode 100644 backend/src/db/models/returns.js rename backend/src/db/models/{product_variants.js => reviews.js} (50%) create mode 100644 backend/src/db/models/tags.js create mode 100644 backend/src/routes/dashboard.js rename backend/src/routes/{product_variants.js => discounts.js} (75%) create mode 100644 backend/src/routes/inventory_movements.js create mode 100644 backend/src/routes/returns.js create mode 100644 backend/src/routes/reviews.js rename backend/src/routes/{inventory.js => tags.js} (78%) rename backend/src/services/{inventory.js => discounts.js} (85%) create mode 100644 backend/src/services/inventory_movements.js rename backend/src/services/{product_variants.js => returns.js} (82%) create mode 100644 backend/src/services/reviews.js create mode 100644 backend/src/services/tags.js create mode 100644 frontend/src/components/Discounts/CardDiscounts.tsx create mode 100644 frontend/src/components/Discounts/ListDiscounts.tsx rename frontend/src/components/{Product_variants/TableProduct_variants.tsx => Discounts/TableDiscounts.tsx} (95%) create mode 100644 frontend/src/components/Discounts/configureDiscountsCols.tsx delete mode 100644 frontend/src/components/Inventory/CardInventory.tsx create mode 100644 frontend/src/components/Inventory_movements/CardInventory_movements.tsx create mode 100644 frontend/src/components/Inventory_movements/ListInventory_movements.tsx create mode 100644 frontend/src/components/Inventory_movements/TableInventory_movements.tsx create mode 100644 frontend/src/components/Inventory_movements/configureInventory_movementsCols.tsx delete mode 100644 frontend/src/components/Product_variants/ListProduct_variants.tsx delete mode 100644 frontend/src/components/Product_variants/configureProduct_variantsCols.tsx create mode 100644 frontend/src/components/Returns/CardReturns.tsx create mode 100644 frontend/src/components/Returns/ListReturns.tsx create mode 100644 frontend/src/components/Returns/TableReturns.tsx create mode 100644 frontend/src/components/Returns/configureReturnsCols.tsx create mode 100644 frontend/src/components/Reviews/CardReviews.tsx create mode 100644 frontend/src/components/Reviews/ListReviews.tsx rename frontend/src/components/{Inventory/TableInventory.tsx => Reviews/TableReviews.tsx} (95%) create mode 100644 frontend/src/components/Reviews/configureReviewsCols.tsx rename frontend/src/components/{Product_variants/CardProduct_variants.tsx => Tags/CardTags.tsx} (68%) rename frontend/src/components/{Inventory/ListInventory.tsx => Tags/ListTags.tsx} (66%) create mode 100644 frontend/src/components/Tags/TableTags.tsx rename frontend/src/components/{Inventory/configureInventoryCols.tsx => Tags/configureTagsCols.tsx} (65%) create mode 100644 frontend/src/pages/discounts/[discountsId].tsx create mode 100644 frontend/src/pages/discounts/discounts-edit.tsx rename frontend/src/pages/{product_variants/product_variants-list.tsx => discounts/discounts-list.tsx} (78%) create mode 100644 frontend/src/pages/discounts/discounts-new.tsx create mode 100644 frontend/src/pages/discounts/discounts-table.tsx create mode 100644 frontend/src/pages/discounts/discounts-view.tsx delete mode 100644 frontend/src/pages/inventory/[inventoryId].tsx delete mode 100644 frontend/src/pages/inventory/inventory-edit.tsx create mode 100644 frontend/src/pages/inventory_movements/[inventory_movementsId].tsx create mode 100644 frontend/src/pages/inventory_movements/inventory_movements-edit.tsx create mode 100644 frontend/src/pages/inventory_movements/inventory_movements-list.tsx create mode 100644 frontend/src/pages/inventory_movements/inventory_movements-new.tsx create mode 100644 frontend/src/pages/inventory_movements/inventory_movements-table.tsx create mode 100644 frontend/src/pages/inventory_movements/inventory_movements-view.tsx delete mode 100644 frontend/src/pages/product_variants/product_variants-view.tsx create mode 100644 frontend/src/pages/returns/[returnsId].tsx create mode 100644 frontend/src/pages/returns/returns-edit.tsx create mode 100644 frontend/src/pages/returns/returns-list.tsx create mode 100644 frontend/src/pages/returns/returns-new.tsx create mode 100644 frontend/src/pages/returns/returns-table.tsx create mode 100644 frontend/src/pages/returns/returns-view.tsx create mode 100644 frontend/src/pages/reviews/[reviewsId].tsx create mode 100644 frontend/src/pages/reviews/reviews-edit.tsx create mode 100644 frontend/src/pages/reviews/reviews-list.tsx rename frontend/src/pages/{inventory/inventory-new.tsx => reviews/reviews-new.tsx} (55%) rename frontend/src/pages/{product_variants/product_variants-table.tsx => reviews/reviews-table.tsx} (78%) create mode 100644 frontend/src/pages/reviews/reviews-view.tsx rename frontend/src/pages/{product_variants/[product_variantsId].tsx => tags/[tagsId].tsx} (68%) rename frontend/src/pages/{product_variants/product_variants-edit.tsx => tags/tags-edit.tsx} (71%) rename frontend/src/pages/{inventory/inventory-list.tsx => tags/tags-list.tsx} (82%) rename frontend/src/pages/{product_variants/product_variants-new.tsx => tags/tags-new.tsx} (76%) rename frontend/src/pages/{inventory/inventory-table.tsx => tags/tags-table.tsx} (82%) rename frontend/src/pages/{inventory/inventory-view.tsx => tags/tags-view.tsx} (62%) create mode 100644 frontend/src/stores/dashboardSlice.ts rename frontend/src/stores/{inventory/inventorySlice.ts => discounts/discountsSlice.ts} (79%) create mode 100644 frontend/src/stores/inventory_movements/inventory_movementsSlice.ts rename frontend/src/stores/{product_variants/product_variantsSlice.ts => returns/returnsSlice.ts} (76%) create mode 100644 frontend/src/stores/reviews/reviewsSlice.ts create mode 100644 frontend/src/stores/tags/tagsSlice.ts diff --git a/.gitignore b/.gitignore index e427ff3..35390a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +/backend/node_modules +/frontend/node_modules node_modules/ */node_modules/ -*/build/ +**/node_modules/ +*/build/ \ No newline at end of file diff --git a/502.html b/502.html index 48cba4b..441b7b8 100644 --- a/502.html +++ b/502.html @@ -129,8 +129,8 @@

The application is currently launching. The page will automatically refresh once site is available.

-

StoreOps Manager

-

Manage products, inventory, orders, customers, and payments with Admin and Staff roles.

+

Store Operations Manager

+

Store Operations Manager for catalog and full order lifecycle with products, customers, payments, and reporting.

App Logo CREATE DATABASE db_storeops_manager;` + - `postgres=> CREATE DATABASE db_store_operations_manager;` - Then give that new user privileges to the new database then quit the `psql`. - - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_storeops_manager TO admin;` + - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_store_operations_manager TO admin;` - `postgres=> \q` ------------ diff --git a/backend/package.json b/backend/package.json index 56fa5d3..ffbfb37 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { - "name": "storeopsmanager", - "description": "StoreOps Manager - template backend", + "name": "storeoperationsmanager", + "description": "Store Operations Manager - template backend", "scripts": { "start": "npm run db:migrate && npm run db:seed && npm run watch", "db:migrate": "sequelize-cli db:migrate", diff --git a/backend/src/config.js b/backend/src/config.js index a0294da..927e473 100644 --- a/backend/src/config.js +++ b/backend/src/config.js @@ -11,15 +11,15 @@ const config = { bcrypt: { saltRounds: 12 }, - admin_pass: "fc0de3ba", - user_pass: "64c04c521f22", + admin_pass: "4df3d09c", + user_pass: "4e97b44f02af", admin_email: "admin@flatlogic.com", providers: { LOCAL: 'local', GOOGLE: 'google', MICROSOFT: 'microsoft' }, - secret_key: process.env.SECRET_KEY || 'fc0de3ba-4aee-4507-88c3-64c04c521f22', + secret_key: process.env.SECRET_KEY || '4df3d09c-78bc-4451-8952-4e97b44f02af', remote: '', port: process.env.NODE_ENV === "production" ? "" : "8080", hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost", @@ -39,7 +39,7 @@ const config = { }, uploadDir: os.tmpdir(), email: { - from: 'StoreOps Manager ', + from: 'Store Operations Manager ', host: 'email-smtp.us-east-1.amazonaws.com', port: 587, auth: { @@ -60,7 +60,7 @@ const config = { }, - project_uuid: 'fc0de3ba-4aee-4507-88c3-64c04c521f22', + project_uuid: '4df3d09c-78bc-4451-8952-4e97b44f02af', flHost: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects', @@ -69,7 +69,7 @@ const config = { config.pexelsKey = process.env.PEXELS_KEY || ''; -config.pexelsQuery = 'sunrise over calm ocean with birds'; +config.pexelsQuery = 'compass on vintage map'; config.host = process.env.NODE_ENV === "production" ? config.remote : "http://localhost"; config.apiUrl = `${config.host}${config.port ? `:${config.port}` : ``}/api`; config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`; diff --git a/backend/src/db/api/categories.js b/backend/src/db/api/categories.js index b1fa049..a4b85ef 100644 --- a/backend/src/db/api/categories.js +++ b/backend/src/db/api/categories.js @@ -195,6 +195,8 @@ module.exports = class CategoriesDBApi { + + output.products_category = await categories.getProducts_category({ transaction }); @@ -208,6 +210,7 @@ module.exports = class CategoriesDBApi { + output.parent = await categories.getParent({ transaction }); diff --git a/backend/src/db/api/customers.js b/backend/src/db/api/customers.js index 4d242ca..1a9d4df 100644 --- a/backend/src/db/api/customers.js +++ b/backend/src/db/api/customers.js @@ -36,12 +36,12 @@ module.exports = class CustomersDBApi { null , - billing_address: data.billing_address + company: data.company || null , - shipping_address: data.shipping_address + address: data.address || null , @@ -51,10 +51,9 @@ module.exports = class CustomersDBApi { null , - vip: data.vip + loyalty_points: data.loyalty_points || - false - + null , importHash: data.importHash || null, @@ -97,12 +96,12 @@ module.exports = class CustomersDBApi { null , - billing_address: item.billing_address + company: item.company || null , - shipping_address: item.shipping_address + address: item.address || null , @@ -112,10 +111,9 @@ module.exports = class CustomersDBApi { null , - vip: item.vip + loyalty_points: item.loyalty_points || - false - + null , importHash: item.importHash || null, @@ -154,16 +152,16 @@ module.exports = class CustomersDBApi { if (data.phone !== undefined) updatePayload.phone = data.phone; - if (data.billing_address !== undefined) updatePayload.billing_address = data.billing_address; + if (data.company !== undefined) updatePayload.company = data.company; - if (data.shipping_address !== undefined) updatePayload.shipping_address = data.shipping_address; + if (data.address !== undefined) updatePayload.address = data.address; if (data.notes !== undefined) updatePayload.notes = data.notes; - if (data.vip !== undefined) updatePayload.vip = data.vip; + if (data.loyalty_points !== undefined) updatePayload.loyalty_points = data.loyalty_points; updatePayload.updatedById = currentUser.id; @@ -252,6 +250,9 @@ module.exports = class CustomersDBApi { + + + output.orders_customer = await customers.getOrders_customer({ transaction }); @@ -333,24 +334,24 @@ module.exports = class CustomersDBApi { }; } - if (filter.billing_address) { + if (filter.company) { where = { ...where, [Op.and]: Utils.ilike( 'customers', - 'billing_address', - filter.billing_address, + 'company', + filter.company, ), }; } - if (filter.shipping_address) { + if (filter.address) { where = { ...where, [Op.and]: Utils.ilike( 'customers', - 'shipping_address', - filter.shipping_address, + 'address', + filter.address, ), }; } @@ -371,6 +372,30 @@ module.exports = class CustomersDBApi { + if (filter.loyalty_pointsRange) { + const [start, end] = filter.loyalty_pointsRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + loyalty_points: { + ...where.loyalty_points, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + loyalty_points: { + ...where.loyalty_points, + [Op.lte]: end, + }, + }; + } + } + if (filter.active !== undefined) { where = { @@ -380,13 +405,6 @@ module.exports = class CustomersDBApi { } - if (filter.vip) { - where = { - ...where, - vip: filter.vip, - }; - } - diff --git a/backend/src/db/api/discounts.js b/backend/src/db/api/discounts.js new file mode 100644 index 0000000..5912685 --- /dev/null +++ b/backend/src/db/api/discounts.js @@ -0,0 +1,519 @@ + +const db = require('../models'); +const FileDBApi = require('./file'); +const crypto = require('crypto'); +const Utils = require('../utils'); + + + +const Sequelize = db.Sequelize; +const Op = Sequelize.Op; + +module.exports = class DiscountsDBApi { + + + + static async create(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const discounts = await db.discounts.create( + { + id: data.id || undefined, + + code: data.code + || + null + , + + description: data.description + || + null + , + + type: data.type + || + null + , + + value: data.value + || + null + , + + active: data.active + || + false + + , + + starts: data.starts + || + null + , + + ends: data.ends + || + null + , + + importHash: data.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + }, + { transaction }, + ); + + + + + + + + return discounts; + } + + + static async bulkImport(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + // Prepare data - wrapping individual data transformations in a map() method + const discountsData = data.map((item, index) => ({ + id: item.id || undefined, + + code: item.code + || + null + , + + description: item.description + || + null + , + + type: item.type + || + null + , + + value: item.value + || + null + , + + active: item.active + || + false + + , + + starts: item.starts + || + null + , + + ends: item.ends + || + null + , + + importHash: item.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + createdAt: new Date(Date.now() + index * 1000), + })); + + // Bulk create items + const discounts = await db.discounts.bulkCreate(discountsData, { transaction }); + + // For each item created, replace relation files + + + return discounts; + } + + static async update(id, data, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + + + const discounts = await db.discounts.findByPk(id, {}, {transaction}); + + + + + const updatePayload = {}; + + if (data.code !== undefined) updatePayload.code = data.code; + + + if (data.description !== undefined) updatePayload.description = data.description; + + + if (data.type !== undefined) updatePayload.type = data.type; + + + if (data.value !== undefined) updatePayload.value = data.value; + + + if (data.active !== undefined) updatePayload.active = data.active; + + + if (data.starts !== undefined) updatePayload.starts = data.starts; + + + if (data.ends !== undefined) updatePayload.ends = data.ends; + + + updatePayload.updatedById = currentUser.id; + + await discounts.update(updatePayload, {transaction}); + + + + + + + + + + return discounts; + } + + static async deleteByIds(ids, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const discounts = await db.discounts.findAll({ + where: { + id: { + [Op.in]: ids, + }, + }, + transaction, + }); + + await db.sequelize.transaction(async (transaction) => { + for (const record of discounts) { + await record.update( + {deletedBy: currentUser.id}, + {transaction} + ); + } + for (const record of discounts) { + await record.destroy({transaction}); + } + }); + + + return discounts; + } + + static async remove(id, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + + const discounts = await db.discounts.findByPk(id, options); + + await discounts.update({ + deletedBy: currentUser.id + }, { + transaction, + }); + + await discounts.destroy({ + transaction + }); + + return discounts; + } + + static async findBy(where, options) { + const transaction = (options && options.transaction) || undefined; + + const discounts = await db.discounts.findOne( + { where }, + { transaction }, + ); + + if (!discounts) { + return discounts; + } + + const output = discounts.get({plain: true}); + + + + + + + + + + + + + + + + + + + + + return output; + } + + static async findAll( + filter, + options + ) { + const limit = filter.limit || 0; + let offset = 0; + let where = {}; + const currentPage = +filter.page; + + + + + + offset = currentPage * limit; + + const orderBy = null; + + const transaction = (options && options.transaction) || undefined; + + let include = [ + + + + ]; + + if (filter) { + if (filter.id) { + where = { + ...where, + ['id']: Utils.uuid(filter.id), + }; + } + + + if (filter.code) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'discounts', + 'code', + filter.code, + ), + }; + } + + if (filter.description) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'discounts', + 'description', + filter.description, + ), + }; + } + + + + + + + if (filter.valueRange) { + const [start, end] = filter.valueRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + value: { + ...where.value, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + value: { + ...where.value, + [Op.lte]: end, + }, + }; + } + } + + if (filter.startsRange) { + const [start, end] = filter.startsRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + starts: { + ...where.starts, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + starts: { + ...where.starts, + [Op.lte]: end, + }, + }; + } + } + + if (filter.endsRange) { + const [start, end] = filter.endsRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + ends: { + ...where.ends, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + ends: { + ...where.ends, + [Op.lte]: end, + }, + }; + } + } + + + if (filter.active !== undefined) { + where = { + ...where, + active: filter.active === true || filter.active === 'true' + }; + } + + + if (filter.type) { + where = { + ...where, + type: filter.type, + }; + } + + if (filter.active) { + where = { + ...where, + active: filter.active, + }; + } + + + + + + if (filter.createdAtRange) { + const [start, end] = filter.createdAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.lte]: end, + }, + }; + } + } + } + + + + + const queryOptions = { + where, + include, + distinct: true, + order: filter.field && filter.sort + ? [[filter.field, filter.sort]] + : [['createdAt', 'desc']], + transaction: options?.transaction, + logging: console.log + }; + + if (!options?.countOnly) { + queryOptions.limit = limit ? Number(limit) : undefined; + queryOptions.offset = offset ? Number(offset) : undefined; + } + + try { + const { rows, count } = await db.discounts.findAndCountAll(queryOptions); + + return { + rows: options?.countOnly ? [] : rows, + count: count + }; + } catch (error) { + console.error('Error executing query:', error); + throw error; + } + } + + static async findAllAutocomplete(query, limit, offset, ) { + let where = {}; + + + + if (query) { + where = { + [Op.or]: [ + { ['id']: Utils.uuid(query) }, + Utils.ilike( + 'discounts', + 'code', + query, + ), + ], + }; + } + + const records = await db.discounts.findAll({ + attributes: [ 'id', 'code' ], + where, + limit: limit ? Number(limit) : undefined, + offset: offset ? Number(offset) : undefined, + orderBy: [['code', 'ASC']], + }); + + return records.map((record) => ({ + id: record.id, + label: record.code, + })); + } + + +}; + diff --git a/backend/src/db/api/inventory.js b/backend/src/db/api/inventory_movements.js similarity index 69% rename from backend/src/db/api/inventory.js rename to backend/src/db/api/inventory_movements.js index 8c27191..937859f 100644 --- a/backend/src/db/api/inventory.js +++ b/backend/src/db/api/inventory_movements.js @@ -9,7 +9,7 @@ const Utils = require('../utils'); const Sequelize = db.Sequelize; const Op = Sequelize.Op; -module.exports = class InventoryDBApi { +module.exports = class Inventory_movementsDBApi { @@ -17,27 +17,27 @@ module.exports = class InventoryDBApi { const currentUser = (options && options.currentUser) || { id: null }; const transaction = (options && options.transaction) || undefined; - const inventory = await db.inventory.create( + const inventory_movements = await db.inventory_movements.create( { id: data.id || undefined, - quantity: data.quantity - || - null - , - - location: data.location - || - null - , - - change_type: data.change_type - || - null - , - note: data.note || + null + , + + change: data.change + || + null + , + + reason: data.reason + || + null + , + + date: data.date + || null , @@ -49,7 +49,7 @@ module.exports = class InventoryDBApi { ); - await inventory.setVariant( data.variant || null, { + await inventory_movements.setProduct( data.product || null, { transaction, }); @@ -58,7 +58,7 @@ module.exports = class InventoryDBApi { - return inventory; + return inventory_movements; } @@ -67,27 +67,27 @@ module.exports = class InventoryDBApi { const transaction = (options && options.transaction) || undefined; // Prepare data - wrapping individual data transformations in a map() method - const inventoryData = data.map((item, index) => ({ + const inventory_movementsData = data.map((item, index) => ({ id: item.id || undefined, - quantity: item.quantity - || - null - , - - location: item.location - || - null - , - - change_type: item.change_type - || - null - , - note: item.note || null + , + + change: item.change + || + null + , + + reason: item.reason + || + null + , + + date: item.date + || + null , importHash: item.importHash || null, @@ -97,12 +97,12 @@ module.exports = class InventoryDBApi { })); // Bulk create items - const inventory = await db.inventory.bulkCreate(inventoryData, { transaction }); + const inventory_movements = await db.inventory_movements.bulkCreate(inventory_movementsData, { transaction }); // For each item created, replace relation files - return inventory; + return inventory_movements; } static async update(id, data, options) { @@ -110,35 +110,35 @@ module.exports = class InventoryDBApi { const transaction = (options && options.transaction) || undefined; - const inventory = await db.inventory.findByPk(id, {}, {transaction}); + const inventory_movements = await db.inventory_movements.findByPk(id, {}, {transaction}); const updatePayload = {}; - if (data.quantity !== undefined) updatePayload.quantity = data.quantity; - - - if (data.location !== undefined) updatePayload.location = data.location; - - - if (data.change_type !== undefined) updatePayload.change_type = data.change_type; - - if (data.note !== undefined) updatePayload.note = data.note; + if (data.change !== undefined) updatePayload.change = data.change; + + + if (data.reason !== undefined) updatePayload.reason = data.reason; + + + if (data.date !== undefined) updatePayload.date = data.date; + + updatePayload.updatedById = currentUser.id; - await inventory.update(updatePayload, {transaction}); + await inventory_movements.update(updatePayload, {transaction}); - if (data.variant !== undefined) { - await inventory.setVariant( + if (data.product !== undefined) { + await inventory_movements.setProduct( - data.variant, + data.product, { transaction } ); @@ -150,14 +150,14 @@ module.exports = class InventoryDBApi { - return inventory; + return inventory_movements; } static async deleteByIds(ids, options) { const currentUser = (options && options.currentUser) || { id: null }; const transaction = (options && options.transaction) || undefined; - const inventory = await db.inventory.findAll({ + const inventory_movements = await db.inventory_movements.findAll({ where: { id: { [Op.in]: ids, @@ -167,53 +167,53 @@ module.exports = class InventoryDBApi { }); await db.sequelize.transaction(async (transaction) => { - for (const record of inventory) { + for (const record of inventory_movements) { await record.update( {deletedBy: currentUser.id}, {transaction} ); } - for (const record of inventory) { + for (const record of inventory_movements) { await record.destroy({transaction}); } }); - return inventory; + return inventory_movements; } static async remove(id, options) { const currentUser = (options && options.currentUser) || {id: null}; const transaction = (options && options.transaction) || undefined; - const inventory = await db.inventory.findByPk(id, options); + const inventory_movements = await db.inventory_movements.findByPk(id, options); - await inventory.update({ + await inventory_movements.update({ deletedBy: currentUser.id }, { transaction, }); - await inventory.destroy({ + await inventory_movements.destroy({ transaction }); - return inventory; + return inventory_movements; } static async findBy(where, options) { const transaction = (options && options.transaction) || undefined; - const inventory = await db.inventory.findOne( + const inventory_movements = await db.inventory_movements.findOne( { where }, { transaction }, ); - if (!inventory) { - return inventory; + if (!inventory_movements) { + return inventory_movements; } - const output = inventory.get({plain: true}); + const output = inventory_movements.get({plain: true}); @@ -230,7 +230,10 @@ module.exports = class InventoryDBApi { - output.variant = await inventory.getVariant({ + + + + output.product = await inventory_movements.getProduct({ transaction }); @@ -261,15 +264,15 @@ module.exports = class InventoryDBApi { let include = [ { - model: db.product_variants, - as: 'variant', + model: db.products, + as: 'product', - where: filter.variant ? { + where: filter.product ? { [Op.or]: [ - { id: { [Op.in]: filter.variant.split('|').map(term => Utils.uuid(term)) } }, + { id: { [Op.in]: filter.product.split('|').map(term => Utils.uuid(term)) } }, { - sku: { - [Op.or]: filter.variant.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) + name: { + [Op.or]: filter.product.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) } }, ] @@ -290,22 +293,11 @@ module.exports = class InventoryDBApi { } - if (filter.location) { - where = { - ...where, - [Op.and]: Utils.ilike( - 'inventory', - 'location', - filter.location, - ), - }; - } - if (filter.note) { where = { ...where, [Op.and]: Utils.ilike( - 'inventory', + 'inventory_movements', 'note', filter.note, ), @@ -317,14 +309,14 @@ module.exports = class InventoryDBApi { - if (filter.quantityRange) { - const [start, end] = filter.quantityRange; + if (filter.changeRange) { + const [start, end] = filter.changeRange; if (start !== undefined && start !== null && start !== '') { where = { ...where, - quantity: { - ...where.quantity, + change: { + ...where.change, [Op.gte]: start, }, }; @@ -333,8 +325,32 @@ module.exports = class InventoryDBApi { if (end !== undefined && end !== null && end !== '') { where = { ...where, - quantity: { - ...where.quantity, + change: { + ...where.change, + [Op.lte]: end, + }, + }; + } + } + + if (filter.dateRange) { + const [start, end] = filter.dateRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + date: { + ...where.date, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + date: { + ...where.date, [Op.lte]: end, }, }; @@ -350,10 +366,10 @@ module.exports = class InventoryDBApi { } - if (filter.change_type) { + if (filter.reason) { where = { ...where, - change_type: filter.change_type, + reason: filter.reason, }; } @@ -408,7 +424,7 @@ module.exports = class InventoryDBApi { } try { - const { rows, count } = await db.inventory.findAndCountAll(queryOptions); + const { rows, count } = await db.inventory_movements.findAndCountAll(queryOptions); return { rows: options?.countOnly ? [] : rows, @@ -430,25 +446,25 @@ module.exports = class InventoryDBApi { [Op.or]: [ { ['id']: Utils.uuid(query) }, Utils.ilike( - 'inventory', - 'location', + 'inventory_movements', + 'note', query, ), ], }; } - const records = await db.inventory.findAll({ - attributes: [ 'id', 'location' ], + const records = await db.inventory_movements.findAll({ + attributes: [ 'id', 'note' ], where, limit: limit ? Number(limit) : undefined, offset: offset ? Number(offset) : undefined, - orderBy: [['location', 'ASC']], + orderBy: [['note', 'ASC']], }); return records.map((record) => ({ id: record.id, - label: record.location, + label: record.note, })); } diff --git a/backend/src/db/api/order_items.js b/backend/src/db/api/order_items.js index 56eb4ca..2deaef4 100644 --- a/backend/src/db/api/order_items.js +++ b/backend/src/db/api/order_items.js @@ -21,7 +21,7 @@ module.exports = class Order_itemsDBApi { { id: data.id || undefined, - name: data.name + product_name: data.product_name || null , @@ -38,6 +38,11 @@ module.exports = class Order_itemsDBApi { total: data.total || + null + , + + sku: data.sku + || null , @@ -49,11 +54,7 @@ module.exports = class Order_itemsDBApi { ); - await order_items.setOrder( data.order || null, { - transaction, - }); - - await order_items.setProduct_variant( data.product_variant || null, { + await order_items.setProduct( data.product || null, { transaction, }); @@ -74,7 +75,7 @@ module.exports = class Order_itemsDBApi { const order_itemsData = data.map((item, index) => ({ id: item.id || undefined, - name: item.name + product_name: item.product_name || null , @@ -92,6 +93,11 @@ module.exports = class Order_itemsDBApi { total: item.total || null + , + + sku: item.sku + || + null , importHash: item.importHash || null, @@ -121,7 +127,7 @@ module.exports = class Order_itemsDBApi { const updatePayload = {}; - if (data.name !== undefined) updatePayload.name = data.name; + if (data.product_name !== undefined) updatePayload.product_name = data.product_name; if (data.quantity !== undefined) updatePayload.quantity = data.quantity; @@ -133,25 +139,19 @@ module.exports = class Order_itemsDBApi { if (data.total !== undefined) updatePayload.total = data.total; + if (data.sku !== undefined) updatePayload.sku = data.sku; + + updatePayload.updatedById = currentUser.id; await order_items.update(updatePayload, {transaction}); - if (data.order !== undefined) { - await order_items.setOrder( + if (data.product !== undefined) { + await order_items.setProduct( - data.order, - - { transaction } - ); - } - - if (data.product_variant !== undefined) { - await order_items.setProduct_variant( - - data.product_variant, + data.product, { transaction } ); @@ -243,12 +243,10 @@ module.exports = class Order_itemsDBApi { - output.order = await order_items.getOrder({ - transaction - }); - output.product_variant = await order_items.getProduct_variant({ + + output.product = await order_items.getProduct({ transaction }); @@ -279,32 +277,15 @@ module.exports = class Order_itemsDBApi { let include = [ { - model: db.orders, - as: 'order', + model: db.products, + as: 'product', - where: filter.order ? { + where: filter.product ? { [Op.or]: [ - { id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } }, + { id: { [Op.in]: filter.product.split('|').map(term => Utils.uuid(term)) } }, { - order_number: { - [Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - { - model: db.product_variants, - as: 'product_variant', - - where: filter.product_variant ? { - [Op.or]: [ - { id: { [Op.in]: filter.product_variant.split('|').map(term => Utils.uuid(term)) } }, - { - sku: { - [Op.or]: filter.product_variant.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) + name: { + [Op.or]: filter.product.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) } }, ] @@ -325,13 +306,24 @@ module.exports = class Order_itemsDBApi { } - if (filter.name) { + if (filter.product_name) { where = { ...where, [Op.and]: Utils.ilike( 'order_items', - 'name', - filter.name, + 'product_name', + filter.product_name, + ), + }; + } + + if (filter.sku) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'order_items', + 'sku', + filter.sku, ), }; } @@ -426,8 +418,6 @@ module.exports = class Order_itemsDBApi { - - if (filter.createdAtRange) { @@ -498,7 +488,7 @@ module.exports = class Order_itemsDBApi { { ['id']: Utils.uuid(query) }, Utils.ilike( 'order_items', - 'name', + 'product_name', query, ), ], @@ -506,16 +496,16 @@ module.exports = class Order_itemsDBApi { } const records = await db.order_items.findAll({ - attributes: [ 'id', 'name' ], + attributes: [ 'id', 'product_name' ], where, limit: limit ? Number(limit) : undefined, offset: offset ? Number(offset) : undefined, - orderBy: [['name', 'ASC']], + orderBy: [['product_name', 'ASC']], }); return records.map((record) => ({ id: record.id, - label: record.name, + label: record.product_name, })); } diff --git a/backend/src/db/api/orders.js b/backend/src/db/api/orders.js index 3ab7a8f..03e1510 100644 --- a/backend/src/db/api/orders.js +++ b/backend/src/db/api/orders.js @@ -31,28 +31,43 @@ module.exports = class OrdersDBApi { null , + order_date: data.order_date + || + null + , + + delivery_date: data.delivery_date + || + null + , + + subtotal: data.subtotal + || + null + , + + tax: data.tax + || + null + , + + shipping_fee: data.shipping_fee + || + null + , + total: data.total || null , - placed_at: data.placed_at - || - null - , - - shipped_at: data.shipped_at - || - null - , - - payment_status: data.payment_status - || - null - , - shipping_address: data.shipping_address || + null + , + + billing_address: data.billing_address + || null , @@ -68,8 +83,24 @@ module.exports = class OrdersDBApi { transaction, }); + await orders.setAssigned_to( data.assigned_to || null, { + transaction, + }); + + await orders.setItems(data.items || [], { + transaction, + }); + + await orders.setPayments(data.payments || [], { + transaction, + }); + + await orders.setShipments(data.shipments || [], { + transaction, + }); + @@ -93,31 +124,46 @@ module.exports = class OrdersDBApi { status: item.status || null + , + + order_date: item.order_date + || + null + , + + delivery_date: item.delivery_date + || + null + , + + subtotal: item.subtotal + || + null + , + + tax: item.tax + || + null + , + + shipping_fee: item.shipping_fee + || + null , total: item.total || null - , - - placed_at: item.placed_at - || - null - , - - shipped_at: item.shipped_at - || - null - , - - payment_status: item.payment_status - || - null , shipping_address: item.shipping_address || null + , + + billing_address: item.billing_address + || + null , importHash: item.importHash || null, @@ -153,21 +199,30 @@ module.exports = class OrdersDBApi { if (data.status !== undefined) updatePayload.status = data.status; + if (data.order_date !== undefined) updatePayload.order_date = data.order_date; + + + if (data.delivery_date !== undefined) updatePayload.delivery_date = data.delivery_date; + + + if (data.subtotal !== undefined) updatePayload.subtotal = data.subtotal; + + + if (data.tax !== undefined) updatePayload.tax = data.tax; + + + if (data.shipping_fee !== undefined) updatePayload.shipping_fee = data.shipping_fee; + + if (data.total !== undefined) updatePayload.total = data.total; - if (data.placed_at !== undefined) updatePayload.placed_at = data.placed_at; - - - if (data.shipped_at !== undefined) updatePayload.shipped_at = data.shipped_at; - - - if (data.payment_status !== undefined) updatePayload.payment_status = data.payment_status; - - if (data.shipping_address !== undefined) updatePayload.shipping_address = data.shipping_address; + if (data.billing_address !== undefined) updatePayload.billing_address = data.billing_address; + + updatePayload.updatedById = currentUser.id; await orders.update(updatePayload, {transaction}); @@ -183,9 +238,30 @@ module.exports = class OrdersDBApi { ); } + if (data.assigned_to !== undefined) { + await orders.setAssigned_to( + + data.assigned_to, + + { transaction } + ); + } + + if (data.items !== undefined) { + await orders.setItems(data.items, { transaction }); + } + + if (data.payments !== undefined) { + await orders.setPayments(data.payments, { transaction }); + } + + if (data.shipments !== undefined) { + await orders.setShipments(data.shipments, { transaction }); + } + @@ -264,28 +340,43 @@ module.exports = class OrdersDBApi { - output.order_items_order = await orders.getOrder_items_order({ + + + + + + + output.returns_order = await orders.getReturns_order({ transaction }); - output.payments_order = await orders.getPayments_order({ - transaction - }); - - - output.shipments_order = await orders.getShipments_order({ - transaction - }); - - - output.customer = await orders.getCustomer({ transaction }); + output.items = await orders.getItems({ + transaction + }); + + + output.payments = await orders.getPayments({ + transaction + }); + + + output.shipments = await orders.getShipments({ + transaction + }); + + + output.assigned_to = await orders.getAssigned_to({ + transaction + }); + + return output; } @@ -328,6 +419,41 @@ module.exports = class OrdersDBApi { }, + { + model: db.users, + as: 'assigned_to', + + where: filter.assigned_to ? { + [Op.or]: [ + { id: { [Op.in]: filter.assigned_to.split('|').map(term => Utils.uuid(term)) } }, + { + firstName: { + [Op.or]: filter.assigned_to.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) + } + }, + ] + } : {}, + + }, + + + { + model: db.order_items, + as: 'items', + required: false, + }, + + { + model: db.payments, + as: 'payments', + required: false, + }, + + { + model: db.shipments, + as: 'shipments', + required: false, + }, ]; @@ -363,11 +489,160 @@ module.exports = class OrdersDBApi { }; } + if (filter.billing_address) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'orders', + 'billing_address', + filter.billing_address, + ), + }; + } + + if (filter.calendarStart && filter.calendarEnd) { + where = { + ...where, + [Op.or]: [ + { + order_date: { + [Op.between]: [filter.calendarStart, filter.calendarEnd], + }, + }, + { + delivery_date: { + [Op.between]: [filter.calendarStart, filter.calendarEnd], + }, + }, + ], + }; + } + + if (filter.order_dateRange) { + const [start, end] = filter.order_dateRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + order_date: { + ...where.order_date, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + order_date: { + ...where.order_date, + [Op.lte]: end, + }, + }; + } + } + + if (filter.delivery_dateRange) { + const [start, end] = filter.delivery_dateRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + delivery_date: { + ...where.delivery_date, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + delivery_date: { + ...where.delivery_date, + [Op.lte]: end, + }, + }; + } + } + + if (filter.subtotalRange) { + const [start, end] = filter.subtotalRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + subtotal: { + ...where.subtotal, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + subtotal: { + ...where.subtotal, + [Op.lte]: end, + }, + }; + } + } + + if (filter.taxRange) { + const [start, end] = filter.taxRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + tax: { + ...where.tax, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + tax: { + ...where.tax, + [Op.lte]: end, + }, + }; + } + } + + if (filter.shipping_feeRange) { + const [start, end] = filter.shipping_feeRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + shipping_fee: { + ...where.shipping_fee, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + shipping_fee: { + ...where.shipping_fee, + [Op.lte]: end, + }, + }; + } + } + if (filter.totalRange) { const [start, end] = filter.totalRange; @@ -392,54 +667,6 @@ module.exports = class OrdersDBApi { } } - if (filter.placed_atRange) { - const [start, end] = filter.placed_atRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - placed_at: { - ...where.placed_at, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - placed_at: { - ...where.placed_at, - [Op.lte]: end, - }, - }; - } - } - - if (filter.shipped_atRange) { - const [start, end] = filter.shipped_atRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - shipped_at: { - ...where.shipped_at, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - shipped_at: { - ...where.shipped_at, - [Op.lte]: end, - }, - }; - } - } - if (filter.active !== undefined) { where = { @@ -456,17 +683,81 @@ module.exports = class OrdersDBApi { }; } - if (filter.payment_status) { - where = { - ...where, - payment_status: filter.payment_status, - }; - } - + + + + if (filter.items) { + const searchTerms = filter.items.split('|'); + + include = [ + { + model: db.order_items, + as: 'items_filter', + required: searchTerms.length > 0, + where: searchTerms.length > 0 ? { + [Op.or]: [ + { id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } }, + { + product_name: { + [Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` })) + } + } + ] + } : undefined + }, + ...include, + ] + } + + if (filter.payments) { + const searchTerms = filter.payments.split('|'); + + include = [ + { + model: db.payments, + as: 'payments_filter', + required: searchTerms.length > 0, + where: searchTerms.length > 0 ? { + [Op.or]: [ + { id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } }, + { + transaction_ref: { + [Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` })) + } + } + ] + } : undefined + }, + ...include, + ] + } + + if (filter.shipments) { + const searchTerms = filter.shipments.split('|'); + + include = [ + { + model: db.shipments, + as: 'shipments_filter', + required: searchTerms.length > 0, + where: searchTerms.length > 0 ? { + [Op.or]: [ + { id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } }, + { + tracking_number: { + [Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` })) + } + } + ] + } : undefined + }, + ...include, + ] + } if (filter.createdAtRange) { diff --git a/backend/src/db/api/payments.js b/backend/src/db/api/payments.js index 1016c9e..b2851d5 100644 --- a/backend/src/db/api/payments.js +++ b/backend/src/db/api/payments.js @@ -21,12 +21,7 @@ module.exports = class PaymentsDBApi { { id: data.id || undefined, - transaction_id: data.transaction_id - || - null - , - - payment_method: data.payment_method + transaction_ref: data.transaction_ref || null , @@ -36,6 +31,11 @@ module.exports = class PaymentsDBApi { null , + method: data.method + || + null + , + status: data.status || null @@ -46,6 +46,17 @@ module.exports = class PaymentsDBApi { null , + provider: data.provider + || + null + , + + refunded: data.refunded + || + false + + , + importHash: data.importHash || null, createdById: currentUser.id, updatedById: currentUser.id, @@ -54,14 +65,20 @@ module.exports = class PaymentsDBApi { ); - await payments.setOrder( data.order || null, { - transaction, - }); - + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.payments.getTableName(), + belongsToColumn: 'receipt', + belongsToId: payments.id, + }, + data.receipt, + options, + ); + return payments; } @@ -75,12 +92,7 @@ module.exports = class PaymentsDBApi { const paymentsData = data.map((item, index) => ({ id: item.id || undefined, - transaction_id: item.transaction_id - || - null - , - - payment_method: item.payment_method + transaction_ref: item.transaction_ref || null , @@ -88,6 +100,11 @@ module.exports = class PaymentsDBApi { amount: item.amount || null + , + + method: item.method + || + null , status: item.status @@ -98,6 +115,17 @@ module.exports = class PaymentsDBApi { paid_at: item.paid_at || null + , + + provider: item.provider + || + null + , + + refunded: item.refunded + || + false + , importHash: item.importHash || null, @@ -111,6 +139,18 @@ module.exports = class PaymentsDBApi { // For each item created, replace relation files + for (let i = 0; i < payments.length; i++) { + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.payments.getTableName(), + belongsToColumn: 'receipt', + belongsToId: payments[i].id, + }, + data[i].receipt, + options, + ); + } + return payments; } @@ -127,41 +167,48 @@ module.exports = class PaymentsDBApi { const updatePayload = {}; - if (data.transaction_id !== undefined) updatePayload.transaction_id = data.transaction_id; - - - if (data.payment_method !== undefined) updatePayload.payment_method = data.payment_method; + if (data.transaction_ref !== undefined) updatePayload.transaction_ref = data.transaction_ref; if (data.amount !== undefined) updatePayload.amount = data.amount; + if (data.method !== undefined) updatePayload.method = data.method; + + if (data.status !== undefined) updatePayload.status = data.status; if (data.paid_at !== undefined) updatePayload.paid_at = data.paid_at; + if (data.provider !== undefined) updatePayload.provider = data.provider; + + + if (data.refunded !== undefined) updatePayload.refunded = data.refunded; + + updatePayload.updatedById = currentUser.id; await payments.update(updatePayload, {transaction}); - if (data.order !== undefined) { - await payments.setOrder( - - data.order, - - { transaction } - ); - } - + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.payments.getTableName(), + belongsToColumn: 'receipt', + belongsToId: payments.id, + }, + data.receipt, + options, + ); + return payments; } @@ -243,7 +290,10 @@ module.exports = class PaymentsDBApi { - output.order = await payments.getOrder({ + + + + output.receipt = await payments.getReceipt({ transaction }); @@ -273,25 +323,13 @@ module.exports = class PaymentsDBApi { let include = [ + + { - model: db.orders, - as: 'order', - - where: filter.order ? { - [Op.or]: [ - { id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } }, - { - order_number: { - [Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - + model: db.file, + as: 'receipt', }, - - ]; if (filter) { @@ -303,13 +341,24 @@ module.exports = class PaymentsDBApi { } - if (filter.transaction_id) { + if (filter.transaction_ref) { where = { ...where, [Op.and]: Utils.ilike( 'payments', - 'transaction_id', - filter.transaction_id, + 'transaction_ref', + filter.transaction_ref, + ), + }; + } + + if (filter.provider) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'payments', + 'provider', + filter.provider, ), }; } @@ -376,10 +425,10 @@ module.exports = class PaymentsDBApi { } - if (filter.payment_method) { + if (filter.method) { where = { ...where, - payment_method: filter.payment_method, + method: filter.method, }; } @@ -390,10 +439,15 @@ module.exports = class PaymentsDBApi { }; } + if (filter.refunded) { + where = { + ...where, + refunded: filter.refunded, + }; + } + - - if (filter.createdAtRange) { @@ -464,7 +518,7 @@ module.exports = class PaymentsDBApi { { ['id']: Utils.uuid(query) }, Utils.ilike( 'payments', - 'transaction_id', + 'transaction_ref', query, ), ], @@ -472,16 +526,16 @@ module.exports = class PaymentsDBApi { } const records = await db.payments.findAll({ - attributes: [ 'id', 'transaction_id' ], + attributes: [ 'id', 'transaction_ref' ], where, limit: limit ? Number(limit) : undefined, offset: offset ? Number(offset) : undefined, - orderBy: [['transaction_id', 'ASC']], + orderBy: [['transaction_ref', 'ASC']], }); return records.map((record) => ({ id: record.id, - label: record.transaction_id, + label: record.transaction_ref, })); } diff --git a/backend/src/db/api/permissions.js b/backend/src/db/api/permissions.js index b5e3986..81c0000 100644 --- a/backend/src/db/api/permissions.js +++ b/backend/src/db/api/permissions.js @@ -178,6 +178,9 @@ module.exports = class PermissionsDBApi { + + + return output; } diff --git a/backend/src/db/api/products.js b/backend/src/db/api/products.js index 6273dbb..237c538 100644 --- a/backend/src/db/api/products.js +++ b/backend/src/db/api/products.js @@ -41,7 +41,17 @@ module.exports = class ProductsDBApi { null , - weight: data.weight + cost: data.cost + || + null + , + + currency: data.currency + || + null + , + + stock: data.stock || null , @@ -64,8 +74,16 @@ module.exports = class ProductsDBApi { transaction, }); + await products.setSupplier( data.supplier || null, { + transaction, + }); + + await products.setTags(data.tags || [], { + transaction, + }); + await FileDBApi.replaceRelationFiles( @@ -111,7 +129,17 @@ module.exports = class ProductsDBApi { null , - weight: item.weight + cost: item.cost + || + null + , + + currency: item.currency + || + null + , + + stock: item.stock || null , @@ -173,7 +201,13 @@ module.exports = class ProductsDBApi { if (data.price !== undefined) updatePayload.price = data.price; - if (data.weight !== undefined) updatePayload.weight = data.weight; + if (data.cost !== undefined) updatePayload.cost = data.cost; + + + if (data.currency !== undefined) updatePayload.currency = data.currency; + + + if (data.stock !== undefined) updatePayload.stock = data.stock; if (data.active !== undefined) updatePayload.active = data.active; @@ -194,9 +228,22 @@ module.exports = class ProductsDBApi { ); } + if (data.supplier !== undefined) { + await products.setSupplier( + + data.supplier, + + { transaction } + ); + } + + if (data.tags !== undefined) { + await products.setTags(data.tags, { transaction }); + } + await FileDBApi.replaceRelationFiles( @@ -282,13 +329,24 @@ module.exports = class ProductsDBApi { - output.product_variants_product = await products.getProduct_variants_product({ + + + output.inventory_movements_product = await products.getInventory_movements_product({ + transaction + }); + + + output.reviews_product = await products.getReviews_product({ transaction }); + output.order_items_product = await products.getOrder_items_product({ + transaction + }); + @@ -304,6 +362,16 @@ module.exports = class ProductsDBApi { }); + output.supplier = await products.getSupplier({ + transaction + }); + + + output.tags = await products.getTags({ + transaction + }); + + return output; } @@ -346,6 +414,29 @@ module.exports = class ProductsDBApi { }, + { + model: db.suppliers, + as: 'supplier', + + where: filter.supplier ? { + [Op.or]: [ + { id: { [Op.in]: filter.supplier.split('|').map(term => Utils.uuid(term)) } }, + { + name: { + [Op.or]: filter.supplier.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) + } + }, + ] + } : {}, + + }, + + + { + model: db.tags, + as: 'tags', + required: false, + }, { @@ -397,6 +488,17 @@ module.exports = class ProductsDBApi { }; } + if (filter.currency) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'products', + 'currency', + filter.currency, + ), + }; + } + @@ -426,14 +528,14 @@ module.exports = class ProductsDBApi { } } - if (filter.weightRange) { - const [start, end] = filter.weightRange; + if (filter.costRange) { + const [start, end] = filter.costRange; if (start !== undefined && start !== null && start !== '') { where = { ...where, - weight: { - ...where.weight, + cost: { + ...where.cost, [Op.gte]: start, }, }; @@ -442,8 +544,32 @@ module.exports = class ProductsDBApi { if (end !== undefined && end !== null && end !== '') { where = { ...where, - weight: { - ...where.weight, + cost: { + ...where.cost, + [Op.lte]: end, + }, + }; + } + } + + if (filter.stockRange) { + const [start, end] = filter.stockRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + stock: { + ...where.stock, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + stock: { + ...where.stock, [Op.lte]: end, }, }; @@ -470,6 +596,31 @@ module.exports = class ProductsDBApi { + + + + if (filter.tags) { + const searchTerms = filter.tags.split('|'); + + include = [ + { + model: db.tags, + as: 'tags_filter', + required: searchTerms.length > 0, + where: searchTerms.length > 0 ? { + [Op.or]: [ + { id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } }, + { + name: { + [Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` })) + } + } + ] + } : undefined + }, + ...include, + ] + } if (filter.createdAtRange) { diff --git a/backend/src/db/api/returns.js b/backend/src/db/api/returns.js new file mode 100644 index 0000000..561df43 --- /dev/null +++ b/backend/src/db/api/returns.js @@ -0,0 +1,552 @@ + +const db = require('../models'); +const FileDBApi = require('./file'); +const crypto = require('crypto'); +const Utils = require('../utils'); + + + +const Sequelize = db.Sequelize; +const Op = Sequelize.Op; + +module.exports = class ReturnsDBApi { + + + + static async create(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const returns = await db.returns.create( + { + id: data.id || undefined, + + rma_number: data.rma_number + || + null + , + + reason: data.reason + || + null + , + + status: data.status + || + null + , + + requested_at: data.requested_at + || + null + , + + processed_at: data.processed_at + || + null + , + + refund_amount: data.refund_amount + || + null + , + + importHash: data.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + }, + { transaction }, + ); + + + await returns.setOrder( data.order || null, { + transaction, + }); + + + + + + + return returns; + } + + + static async bulkImport(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + // Prepare data - wrapping individual data transformations in a map() method + const returnsData = data.map((item, index) => ({ + id: item.id || undefined, + + rma_number: item.rma_number + || + null + , + + reason: item.reason + || + null + , + + status: item.status + || + null + , + + requested_at: item.requested_at + || + null + , + + processed_at: item.processed_at + || + null + , + + refund_amount: item.refund_amount + || + null + , + + importHash: item.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + createdAt: new Date(Date.now() + index * 1000), + })); + + // Bulk create items + const returns = await db.returns.bulkCreate(returnsData, { transaction }); + + // For each item created, replace relation files + + + return returns; + } + + static async update(id, data, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + + + const returns = await db.returns.findByPk(id, {}, {transaction}); + + + + + const updatePayload = {}; + + if (data.rma_number !== undefined) updatePayload.rma_number = data.rma_number; + + + if (data.reason !== undefined) updatePayload.reason = data.reason; + + + if (data.status !== undefined) updatePayload.status = data.status; + + + if (data.requested_at !== undefined) updatePayload.requested_at = data.requested_at; + + + if (data.processed_at !== undefined) updatePayload.processed_at = data.processed_at; + + + if (data.refund_amount !== undefined) updatePayload.refund_amount = data.refund_amount; + + + updatePayload.updatedById = currentUser.id; + + await returns.update(updatePayload, {transaction}); + + + + if (data.order !== undefined) { + await returns.setOrder( + + data.order, + + { transaction } + ); + } + + + + + + + + return returns; + } + + static async deleteByIds(ids, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const returns = await db.returns.findAll({ + where: { + id: { + [Op.in]: ids, + }, + }, + transaction, + }); + + await db.sequelize.transaction(async (transaction) => { + for (const record of returns) { + await record.update( + {deletedBy: currentUser.id}, + {transaction} + ); + } + for (const record of returns) { + await record.destroy({transaction}); + } + }); + + + return returns; + } + + static async remove(id, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + + const returns = await db.returns.findByPk(id, options); + + await returns.update({ + deletedBy: currentUser.id + }, { + transaction, + }); + + await returns.destroy({ + transaction + }); + + return returns; + } + + static async findBy(where, options) { + const transaction = (options && options.transaction) || undefined; + + const returns = await db.returns.findOne( + { where }, + { transaction }, + ); + + if (!returns) { + return returns; + } + + const output = returns.get({plain: true}); + + + + + + + + + + + + + + + + + + + + output.order = await returns.getOrder({ + transaction + }); + + + + return output; + } + + static async findAll( + filter, + options + ) { + const limit = filter.limit || 0; + let offset = 0; + let where = {}; + const currentPage = +filter.page; + + + + + + offset = currentPage * limit; + + const orderBy = null; + + const transaction = (options && options.transaction) || undefined; + + let include = [ + + { + model: db.orders, + as: 'order', + + where: filter.order ? { + [Op.or]: [ + { id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } }, + { + order_number: { + [Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) + } + }, + ] + } : {}, + + }, + + + + ]; + + if (filter) { + if (filter.id) { + where = { + ...where, + ['id']: Utils.uuid(filter.id), + }; + } + + + if (filter.rma_number) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'returns', + 'rma_number', + filter.rma_number, + ), + }; + } + + if (filter.reason) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'returns', + 'reason', + filter.reason, + ), + }; + } + + + + + if (filter.calendarStart && filter.calendarEnd) { + where = { + ...where, + [Op.or]: [ + { + requested_at: { + [Op.between]: [filter.calendarStart, filter.calendarEnd], + }, + }, + { + processed_at: { + [Op.between]: [filter.calendarStart, filter.calendarEnd], + }, + }, + ], + }; + } + + + + if (filter.requested_atRange) { + const [start, end] = filter.requested_atRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + requested_at: { + ...where.requested_at, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + requested_at: { + ...where.requested_at, + [Op.lte]: end, + }, + }; + } + } + + if (filter.processed_atRange) { + const [start, end] = filter.processed_atRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + processed_at: { + ...where.processed_at, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + processed_at: { + ...where.processed_at, + [Op.lte]: end, + }, + }; + } + } + + if (filter.refund_amountRange) { + const [start, end] = filter.refund_amountRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + refund_amount: { + ...where.refund_amount, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + refund_amount: { + ...where.refund_amount, + [Op.lte]: end, + }, + }; + } + } + + + if (filter.active !== undefined) { + where = { + ...where, + active: filter.active === true || filter.active === 'true' + }; + } + + + if (filter.status) { + where = { + ...where, + status: filter.status, + }; + } + + + + + + + + if (filter.createdAtRange) { + const [start, end] = filter.createdAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.lte]: end, + }, + }; + } + } + } + + + + + const queryOptions = { + where, + include, + distinct: true, + order: filter.field && filter.sort + ? [[filter.field, filter.sort]] + : [['createdAt', 'desc']], + transaction: options?.transaction, + logging: console.log + }; + + if (!options?.countOnly) { + queryOptions.limit = limit ? Number(limit) : undefined; + queryOptions.offset = offset ? Number(offset) : undefined; + } + + try { + const { rows, count } = await db.returns.findAndCountAll(queryOptions); + + return { + rows: options?.countOnly ? [] : rows, + count: count + }; + } catch (error) { + console.error('Error executing query:', error); + throw error; + } + } + + static async findAllAutocomplete(query, limit, offset, ) { + let where = {}; + + + + if (query) { + where = { + [Op.or]: [ + { ['id']: Utils.uuid(query) }, + Utils.ilike( + 'returns', + 'rma_number', + query, + ), + ], + }; + } + + const records = await db.returns.findAll({ + attributes: [ 'id', 'rma_number' ], + where, + limit: limit ? Number(limit) : undefined, + offset: offset ? Number(offset) : undefined, + orderBy: [['rma_number', 'ASC']], + }); + + return records.map((record) => ({ + id: record.id, + label: record.rma_number, + })); + } + + +}; + diff --git a/backend/src/db/api/product_variants.js b/backend/src/db/api/reviews.js similarity index 59% rename from backend/src/db/api/product_variants.js rename to backend/src/db/api/reviews.js index 9288954..14bfa40 100644 --- a/backend/src/db/api/product_variants.js +++ b/backend/src/db/api/reviews.js @@ -9,7 +9,7 @@ const Utils = require('../utils'); const Sequelize = db.Sequelize; const Op = Sequelize.Op; -module.exports = class Product_variantsDBApi { +module.exports = class ReviewsDBApi { @@ -17,36 +17,31 @@ module.exports = class Product_variantsDBApi { const currentUser = (options && options.currentUser) || { id: null }; const transaction = (options && options.transaction) || undefined; - const product_variants = await db.product_variants.create( + const reviews = await db.reviews.create( { id: data.id || undefined, - sku: data.sku + author_name: data.author_name || null , - title: data.title + rating: data.rating || null , - price: data.price + comment: data.comment || null , - stock: data.stock + created: data.created || null , - attributes: data.attributes - || - null - , - - active: data.active + approved: data.approved || false @@ -60,11 +55,7 @@ module.exports = class Product_variantsDBApi { ); - await product_variants.setProduct( data.product || null, { - transaction, - }); - - await product_variants.setSupplier( data.supplier || null, { + await reviews.setProduct( data.product || null, { transaction, }); @@ -72,18 +63,8 @@ module.exports = class Product_variantsDBApi { - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.product_variants.getTableName(), - belongsToColumn: 'images', - belongsToId: product_variants.id, - }, - data.images, - options, - ); - - return product_variants; + return reviews; } @@ -92,35 +73,30 @@ module.exports = class Product_variantsDBApi { const transaction = (options && options.transaction) || undefined; // Prepare data - wrapping individual data transformations in a map() method - const product_variantsData = data.map((item, index) => ({ + const reviewsData = data.map((item, index) => ({ id: item.id || undefined, - sku: item.sku + author_name: item.author_name || null , - title: item.title + rating: item.rating || null , - price: item.price + comment: item.comment || null , - stock: item.stock + created: item.created || null , - attributes: item.attributes - || - null - , - - active: item.active + approved: item.approved || false @@ -133,24 +109,12 @@ module.exports = class Product_variantsDBApi { })); // Bulk create items - const product_variants = await db.product_variants.bulkCreate(product_variantsData, { transaction }); + const reviews = await db.reviews.bulkCreate(reviewsData, { transaction }); // For each item created, replace relation files - for (let i = 0; i < product_variants.length; i++) { - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.product_variants.getTableName(), - belongsToColumn: 'images', - belongsToId: product_variants[i].id, - }, - data[i].images, - options, - ); - } - - return product_variants; + return reviews; } static async update(id, data, options) { @@ -158,39 +122,36 @@ module.exports = class Product_variantsDBApi { const transaction = (options && options.transaction) || undefined; - const product_variants = await db.product_variants.findByPk(id, {}, {transaction}); + const reviews = await db.reviews.findByPk(id, {}, {transaction}); const updatePayload = {}; - if (data.sku !== undefined) updatePayload.sku = data.sku; + if (data.author_name !== undefined) updatePayload.author_name = data.author_name; - if (data.title !== undefined) updatePayload.title = data.title; + if (data.rating !== undefined) updatePayload.rating = data.rating; - if (data.price !== undefined) updatePayload.price = data.price; + if (data.comment !== undefined) updatePayload.comment = data.comment; - if (data.stock !== undefined) updatePayload.stock = data.stock; + if (data.created !== undefined) updatePayload.created = data.created; - if (data.attributes !== undefined) updatePayload.attributes = data.attributes; - - - if (data.active !== undefined) updatePayload.active = data.active; + if (data.approved !== undefined) updatePayload.approved = data.approved; updatePayload.updatedById = currentUser.id; - await product_variants.update(updatePayload, {transaction}); + await reviews.update(updatePayload, {transaction}); if (data.product !== undefined) { - await product_variants.setProduct( + await reviews.setProduct( data.product, @@ -198,39 +159,20 @@ module.exports = class Product_variantsDBApi { ); } - if (data.supplier !== undefined) { - await product_variants.setSupplier( - - data.supplier, - - { transaction } - ); - } - - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.product_variants.getTableName(), - belongsToColumn: 'images', - belongsToId: product_variants.id, - }, - data.images, - options, - ); - - return product_variants; + return reviews; } static async deleteByIds(ids, options) { const currentUser = (options && options.currentUser) || { id: null }; const transaction = (options && options.transaction) || undefined; - const product_variants = await db.product_variants.findAll({ + const reviews = await db.reviews.findAll({ where: { id: { [Op.in]: ids, @@ -240,53 +182,53 @@ module.exports = class Product_variantsDBApi { }); await db.sequelize.transaction(async (transaction) => { - for (const record of product_variants) { + for (const record of reviews) { await record.update( {deletedBy: currentUser.id}, {transaction} ); } - for (const record of product_variants) { + for (const record of reviews) { await record.destroy({transaction}); } }); - return product_variants; + return reviews; } static async remove(id, options) { const currentUser = (options && options.currentUser) || {id: null}; const transaction = (options && options.transaction) || undefined; - const product_variants = await db.product_variants.findByPk(id, options); + const reviews = await db.reviews.findByPk(id, options); - await product_variants.update({ + await reviews.update({ deletedBy: currentUser.id }, { transaction, }); - await product_variants.destroy({ + await reviews.destroy({ transaction }); - return product_variants; + return reviews; } static async findBy(where, options) { const transaction = (options && options.transaction) || undefined; - const product_variants = await db.product_variants.findOne( + const reviews = await db.reviews.findOne( { where }, { transaction }, ); - if (!product_variants) { - return product_variants; + if (!reviews) { + return reviews; } - const output = product_variants.get({plain: true}); + const output = reviews.get({plain: true}); @@ -296,32 +238,17 @@ module.exports = class Product_variantsDBApi { - output.inventory_variant = await product_variants.getInventory_variant({ - transaction - }); - - - - output.order_items_product_variant = await product_variants.getOrder_items_product_variant({ - transaction - }); - output.product = await product_variants.getProduct({ - transaction - }); - output.supplier = await product_variants.getSupplier({ - transaction - }); - output.images = await product_variants.getImages({ + output.product = await reviews.getProduct({ transaction }); @@ -368,30 +295,8 @@ module.exports = class Product_variantsDBApi { }, - { - model: db.suppliers, - as: 'supplier', - - where: filter.supplier ? { - [Op.or]: [ - { id: { [Op.in]: filter.supplier.split('|').map(term => Utils.uuid(term)) } }, - { - name: { - [Op.or]: filter.supplier.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - { - model: db.file, - as: 'images', - }, - ]; if (filter) { @@ -403,35 +308,24 @@ module.exports = class Product_variantsDBApi { } - if (filter.sku) { + if (filter.author_name) { where = { ...where, [Op.and]: Utils.ilike( - 'product_variants', - 'sku', - filter.sku, + 'reviews', + 'author_name', + filter.author_name, ), }; } - if (filter.title) { + if (filter.comment) { where = { ...where, [Op.and]: Utils.ilike( - 'product_variants', - 'title', - filter.title, - ), - }; - } - - if (filter.attributes) { - where = { - ...where, - [Op.and]: Utils.ilike( - 'product_variants', - 'attributes', - filter.attributes, + 'reviews', + 'comment', + filter.comment, ), }; } @@ -441,14 +335,14 @@ module.exports = class Product_variantsDBApi { - if (filter.priceRange) { - const [start, end] = filter.priceRange; + if (filter.ratingRange) { + const [start, end] = filter.ratingRange; if (start !== undefined && start !== null && start !== '') { where = { ...where, - price: { - ...where.price, + rating: { + ...where.rating, [Op.gte]: start, }, }; @@ -457,22 +351,22 @@ module.exports = class Product_variantsDBApi { if (end !== undefined && end !== null && end !== '') { where = { ...where, - price: { - ...where.price, + rating: { + ...where.rating, [Op.lte]: end, }, }; } } - if (filter.stockRange) { - const [start, end] = filter.stockRange; + if (filter.createdRange) { + const [start, end] = filter.createdRange; if (start !== undefined && start !== null && start !== '') { where = { ...where, - stock: { - ...where.stock, + created: { + ...where.created, [Op.gte]: start, }, }; @@ -481,8 +375,8 @@ module.exports = class Product_variantsDBApi { if (end !== undefined && end !== null && end !== '') { where = { ...where, - stock: { - ...where.stock, + created: { + ...where.created, [Op.lte]: end, }, }; @@ -498,10 +392,10 @@ module.exports = class Product_variantsDBApi { } - if (filter.active) { + if (filter.approved) { where = { ...where, - active: filter.active, + approved: filter.approved, }; } @@ -509,8 +403,6 @@ module.exports = class Product_variantsDBApi { - - if (filter.createdAtRange) { @@ -558,7 +450,7 @@ module.exports = class Product_variantsDBApi { } try { - const { rows, count } = await db.product_variants.findAndCountAll(queryOptions); + const { rows, count } = await db.reviews.findAndCountAll(queryOptions); return { rows: options?.countOnly ? [] : rows, @@ -580,25 +472,25 @@ module.exports = class Product_variantsDBApi { [Op.or]: [ { ['id']: Utils.uuid(query) }, Utils.ilike( - 'product_variants', - 'sku', + 'reviews', + 'author_name', query, ), ], }; } - const records = await db.product_variants.findAll({ - attributes: [ 'id', 'sku' ], + const records = await db.reviews.findAll({ + attributes: [ 'id', 'author_name' ], where, limit: limit ? Number(limit) : undefined, offset: offset ? Number(offset) : undefined, - orderBy: [['sku', 'ASC']], + orderBy: [['author_name', 'ASC']], }); return records.map((record) => ({ id: record.id, - label: record.sku, + label: record.author_name, })); } diff --git a/backend/src/db/api/roles.js b/backend/src/db/api/roles.js index 3c754ec..3e6b68d 100644 --- a/backend/src/db/api/roles.js +++ b/backend/src/db/api/roles.js @@ -203,6 +203,9 @@ module.exports = class RolesDBApi { + + + output.permissions = await roles.getPermissions({ transaction }); diff --git a/backend/src/db/api/shipments.js b/backend/src/db/api/shipments.js index 0c050df..a133456 100644 --- a/backend/src/db/api/shipments.js +++ b/backend/src/db/api/shipments.js @@ -21,12 +21,12 @@ module.exports = class ShipmentsDBApi { { id: data.id || undefined, - carrier: data.carrier + tracking_number: data.tracking_number || null , - tracking_number: data.tracking_number + carrier: data.carrier || null , @@ -46,7 +46,7 @@ module.exports = class ShipmentsDBApi { null , - cost: data.cost + notes: data.notes || null , @@ -59,10 +59,6 @@ module.exports = class ShipmentsDBApi { ); - await shipments.setOrder( data.order || null, { - transaction, - }); - @@ -80,12 +76,12 @@ module.exports = class ShipmentsDBApi { const shipmentsData = data.map((item, index) => ({ id: item.id || undefined, - carrier: item.carrier + tracking_number: item.tracking_number || null , - tracking_number: item.tracking_number + carrier: item.carrier || null , @@ -105,7 +101,7 @@ module.exports = class ShipmentsDBApi { null , - cost: item.cost + notes: item.notes || null , @@ -137,12 +133,12 @@ module.exports = class ShipmentsDBApi { const updatePayload = {}; - if (data.carrier !== undefined) updatePayload.carrier = data.carrier; - - if (data.tracking_number !== undefined) updatePayload.tracking_number = data.tracking_number; + if (data.carrier !== undefined) updatePayload.carrier = data.carrier; + + if (data.status !== undefined) updatePayload.status = data.status; @@ -152,7 +148,7 @@ module.exports = class ShipmentsDBApi { if (data.delivered_at !== undefined) updatePayload.delivered_at = data.delivered_at; - if (data.cost !== undefined) updatePayload.cost = data.cost; + if (data.notes !== undefined) updatePayload.notes = data.notes; updatePayload.updatedById = currentUser.id; @@ -161,15 +157,6 @@ module.exports = class ShipmentsDBApi { - if (data.order !== undefined) { - await shipments.setOrder( - - data.order, - - { transaction } - ); - } - @@ -256,9 +243,7 @@ module.exports = class ShipmentsDBApi { - output.order = await shipments.getOrder({ - transaction - }); + @@ -286,23 +271,6 @@ module.exports = class ShipmentsDBApi { let include = [ - { - model: db.orders, - as: 'order', - - where: filter.order ? { - [Op.or]: [ - { id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } }, - { - order_number: { - [Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - ]; @@ -316,6 +284,17 @@ module.exports = class ShipmentsDBApi { } + if (filter.tracking_number) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'shipments', + 'tracking_number', + filter.tracking_number, + ), + }; + } + if (filter.carrier) { where = { ...where, @@ -327,13 +306,13 @@ module.exports = class ShipmentsDBApi { }; } - if (filter.tracking_number) { + if (filter.notes) { where = { ...where, [Op.and]: Utils.ilike( 'shipments', - 'tracking_number', - filter.tracking_number, + 'notes', + filter.notes, ), }; } @@ -409,30 +388,6 @@ module.exports = class ShipmentsDBApi { } } - if (filter.costRange) { - const [start, end] = filter.costRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - cost: { - ...where.cost, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - cost: { - ...where.cost, - [Op.lte]: end, - }, - }; - } - } - if (filter.active !== undefined) { where = { @@ -451,8 +406,6 @@ module.exports = class ShipmentsDBApi { - - if (filter.createdAtRange) { diff --git a/backend/src/db/api/suppliers.js b/backend/src/db/api/suppliers.js index 67dc839..732f6d6 100644 --- a/backend/src/db/api/suppliers.js +++ b/backend/src/db/api/suppliers.js @@ -31,12 +31,12 @@ module.exports = class SuppliersDBApi { null , - phone: data.phone + email: data.email || null , - email: data.email + phone: data.phone || null , @@ -86,12 +86,12 @@ module.exports = class SuppliersDBApi { null , - phone: item.phone + email: item.email || null , - email: item.email + phone: item.phone || null , @@ -139,12 +139,12 @@ module.exports = class SuppliersDBApi { if (data.contact_name !== undefined) updatePayload.contact_name = data.contact_name; - if (data.phone !== undefined) updatePayload.phone = data.phone; - - if (data.email !== undefined) updatePayload.email = data.email; + if (data.phone !== undefined) updatePayload.phone = data.phone; + + if (data.address !== undefined) updatePayload.address = data.address; @@ -235,7 +235,8 @@ module.exports = class SuppliersDBApi { - output.product_variants_supplier = await suppliers.getProduct_variants_supplier({ + + output.products_supplier = await suppliers.getProducts_supplier({ transaction }); @@ -247,6 +248,8 @@ module.exports = class SuppliersDBApi { + + return output; } @@ -307,17 +310,6 @@ module.exports = class SuppliersDBApi { }; } - if (filter.phone) { - where = { - ...where, - [Op.and]: Utils.ilike( - 'suppliers', - 'phone', - filter.phone, - ), - }; - } - if (filter.email) { where = { ...where, @@ -329,6 +321,17 @@ module.exports = class SuppliersDBApi { }; } + if (filter.phone) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'suppliers', + 'phone', + filter.phone, + ), + }; + } + if (filter.address) { where = { ...where, diff --git a/backend/src/db/api/tags.js b/backend/src/db/api/tags.js new file mode 100644 index 0000000..41c1c92 --- /dev/null +++ b/backend/src/db/api/tags.js @@ -0,0 +1,366 @@ + +const db = require('../models'); +const FileDBApi = require('./file'); +const crypto = require('crypto'); +const Utils = require('../utils'); + + + +const Sequelize = db.Sequelize; +const Op = Sequelize.Op; + +module.exports = class TagsDBApi { + + + + static async create(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const tags = await db.tags.create( + { + id: data.id || undefined, + + name: data.name + || + null + , + + color: data.color + || + null + , + + importHash: data.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + }, + { transaction }, + ); + + + + + + + + return tags; + } + + + static async bulkImport(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + // Prepare data - wrapping individual data transformations in a map() method + const tagsData = data.map((item, index) => ({ + id: item.id || undefined, + + name: item.name + || + null + , + + color: item.color + || + null + , + + importHash: item.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + createdAt: new Date(Date.now() + index * 1000), + })); + + // Bulk create items + const tags = await db.tags.bulkCreate(tagsData, { transaction }); + + // For each item created, replace relation files + + + return tags; + } + + static async update(id, data, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + + + const tags = await db.tags.findByPk(id, {}, {transaction}); + + + + + const updatePayload = {}; + + if (data.name !== undefined) updatePayload.name = data.name; + + + if (data.color !== undefined) updatePayload.color = data.color; + + + updatePayload.updatedById = currentUser.id; + + await tags.update(updatePayload, {transaction}); + + + + + + + + + + return tags; + } + + static async deleteByIds(ids, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const tags = await db.tags.findAll({ + where: { + id: { + [Op.in]: ids, + }, + }, + transaction, + }); + + await db.sequelize.transaction(async (transaction) => { + for (const record of tags) { + await record.update( + {deletedBy: currentUser.id}, + {transaction} + ); + } + for (const record of tags) { + await record.destroy({transaction}); + } + }); + + + return tags; + } + + static async remove(id, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + + const tags = await db.tags.findByPk(id, options); + + await tags.update({ + deletedBy: currentUser.id + }, { + transaction, + }); + + await tags.destroy({ + transaction + }); + + return tags; + } + + static async findBy(where, options) { + const transaction = (options && options.transaction) || undefined; + + const tags = await db.tags.findOne( + { where }, + { transaction }, + ); + + if (!tags) { + return tags; + } + + const output = tags.get({plain: true}); + + + + + + + + + + + + + + + + + + + + + return output; + } + + static async findAll( + filter, + options + ) { + const limit = filter.limit || 0; + let offset = 0; + let where = {}; + const currentPage = +filter.page; + + + + + + offset = currentPage * limit; + + const orderBy = null; + + const transaction = (options && options.transaction) || undefined; + + let include = [ + + + + ]; + + if (filter) { + if (filter.id) { + where = { + ...where, + ['id']: Utils.uuid(filter.id), + }; + } + + + if (filter.name) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'tags', + 'name', + filter.name, + ), + }; + } + + if (filter.color) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'tags', + 'color', + filter.color, + ), + }; + } + + + + + + + + if (filter.active !== undefined) { + where = { + ...where, + active: filter.active === true || filter.active === 'true' + }; + } + + + + + + + if (filter.createdAtRange) { + const [start, end] = filter.createdAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.lte]: end, + }, + }; + } + } + } + + + + + const queryOptions = { + where, + include, + distinct: true, + order: filter.field && filter.sort + ? [[filter.field, filter.sort]] + : [['createdAt', 'desc']], + transaction: options?.transaction, + logging: console.log + }; + + if (!options?.countOnly) { + queryOptions.limit = limit ? Number(limit) : undefined; + queryOptions.offset = offset ? Number(offset) : undefined; + } + + try { + const { rows, count } = await db.tags.findAndCountAll(queryOptions); + + return { + rows: options?.countOnly ? [] : rows, + count: count + }; + } catch (error) { + console.error('Error executing query:', error); + throw error; + } + } + + static async findAllAutocomplete(query, limit, offset, ) { + let where = {}; + + + + if (query) { + where = { + [Op.or]: [ + { ['id']: Utils.uuid(query) }, + Utils.ilike( + 'tags', + 'name', + query, + ), + ], + }; + } + + const records = await db.tags.findAll({ + attributes: [ 'id', 'name' ], + where, + limit: limit ? Number(limit) : undefined, + offset: offset ? Number(offset) : undefined, + orderBy: [['name', 'ASC']], + }); + + return records.map((record) => ({ + id: record.id, + label: record.name, + })); + } + + +}; + diff --git a/backend/src/db/api/users.js b/backend/src/db/api/users.js index 84a91ff..b44a781 100644 --- a/backend/src/db/api/users.js +++ b/backend/src/db/api/users.js @@ -411,6 +411,13 @@ module.exports = class UsersDBApi { + output.orders_assigned_to = await users.getOrders_assigned_to({ + transaction + }); + + + + diff --git a/backend/src/db/db.config.js b/backend/src/db/db.config.js index d975ec7..ba70dd7 100644 --- a/backend/src/db/db.config.js +++ b/backend/src/db/db.config.js @@ -15,7 +15,7 @@ module.exports = { username: 'postgres', dialect: 'postgres', password: '', - database: 'db_storeops_manager', + database: 'db_store_operations_manager', host: process.env.DB_HOST || 'localhost', logging: console.log, seederStorage: 'sequelize', diff --git a/backend/src/db/migrations/1768218607130.js b/backend/src/db/migrations/1768230063509.js similarity index 77% rename from backend/src/db/migrations/1768218607130.js rename to backend/src/db/migrations/1768230063509.js index 5b8d823..72ad49c 100644 --- a/backend/src/db/migrations/1768218607130.js +++ b/backend/src/db/migrations/1768230063509.js @@ -172,6 +172,70 @@ module.exports = { + await queryInterface.createTable('suppliers', { + id: { + type: Sequelize.DataTypes.UUID, + defaultValue: Sequelize.DataTypes.UUIDV4, + primaryKey: true, + }, + createdById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + updatedById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + createdAt: { type: Sequelize.DataTypes.DATE }, + updatedAt: { type: Sequelize.DataTypes.DATE }, + deletedAt: { type: Sequelize.DataTypes.DATE }, + importHash: { + type: Sequelize.DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, { transaction }); + + + + await queryInterface.createTable('tags', { + id: { + type: Sequelize.DataTypes.UUID, + defaultValue: Sequelize.DataTypes.UUIDV4, + primaryKey: true, + }, + createdById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + updatedById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + createdAt: { type: Sequelize.DataTypes.DATE }, + updatedAt: { type: Sequelize.DataTypes.DATE }, + deletedAt: { type: Sequelize.DataTypes.DATE }, + importHash: { + type: Sequelize.DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, { transaction }); + + + await queryInterface.createTable('products', { id: { type: Sequelize.DataTypes.UUID, @@ -204,7 +268,7 @@ module.exports = { - await queryInterface.createTable('product_variants', { + await queryInterface.createTable('inventory_movements', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, @@ -236,7 +300,39 @@ module.exports = { - await queryInterface.createTable('inventory', { + await queryInterface.createTable('reviews', { + id: { + type: Sequelize.DataTypes.UUID, + defaultValue: Sequelize.DataTypes.UUIDV4, + primaryKey: true, + }, + createdById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + updatedById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + createdAt: { type: Sequelize.DataTypes.DATE }, + updatedAt: { type: Sequelize.DataTypes.DATE }, + deletedAt: { type: Sequelize.DataTypes.DATE }, + importHash: { + type: Sequelize.DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, { transaction }); + + + + await queryInterface.createTable('discounts', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, @@ -396,7 +492,7 @@ module.exports = { - await queryInterface.createTable('suppliers', { + await queryInterface.createTable('returns', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, @@ -733,7 +829,7 @@ module.exports = { await queryInterface.addColumn( 'customers', - 'billing_address', + 'company', { type: Sequelize.DataTypes.TEXT, @@ -748,7 +844,7 @@ module.exports = { await queryInterface.addColumn( 'customers', - 'shipping_address', + 'address', { type: Sequelize.DataTypes.TEXT, @@ -778,12 +874,9 @@ module.exports = { await queryInterface.addColumn( 'customers', - 'vip', + 'loyalty_points', { - type: Sequelize.DataTypes.BOOLEAN, - - defaultValue: false, - allowNull: false, + type: Sequelize.DataTypes.INTEGER, @@ -844,6 +937,126 @@ module.exports = { + await queryInterface.addColumn( + 'suppliers', + 'name', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'suppliers', + 'contact_name', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'suppliers', + 'email', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'suppliers', + 'phone', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'suppliers', + 'address', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'suppliers', + 'notes', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'tags', + 'name', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'tags', + 'color', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + await queryInterface.addColumn( 'products', 'name', @@ -906,7 +1119,7 @@ module.exports = { await queryInterface.addColumn( 'products', - 'weight', + 'cost', { type: Sequelize.DataTypes.DECIMAL, @@ -918,6 +1131,36 @@ module.exports = { + + await queryInterface.addColumn( + 'products', + 'currency', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'products', + 'stock', + { + type: Sequelize.DataTypes.INTEGER, + + + + }, + { transaction } + ); + + + @@ -960,8 +1203,30 @@ module.exports = { await queryInterface.addColumn( - 'product_variants', - 'sku', + 'products', + 'supplierId', + { + type: Sequelize.DataTypes.UUID, + + + + references: { + model: 'suppliers', + key: 'id', + }, + + }, + { transaction } + ); + + + + + + + await queryInterface.addColumn( + 'inventory_movements', + 'note', { type: Sequelize.DataTypes.TEXT, @@ -975,22 +1240,7 @@ module.exports = { await queryInterface.addColumn( - 'product_variants', - 'title', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'product_variants', + 'inventory_movements', 'productId', { type: Sequelize.DataTypes.UUID, @@ -1010,43 +1260,8 @@ module.exports = { await queryInterface.addColumn( - 'product_variants', - 'supplierId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'suppliers', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'product_variants', - 'price', - { - type: Sequelize.DataTypes.DECIMAL, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'product_variants', - 'stock', + 'inventory_movements', + 'change', { type: Sequelize.DataTypes.INTEGER, @@ -1060,8 +1275,40 @@ module.exports = { await queryInterface.addColumn( - 'product_variants', - 'attributes', + 'inventory_movements', + 'reason', + { + type: Sequelize.DataTypes.ENUM, + + + values: ['Purchase','Sale','Return','Adjustment'], + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'inventory_movements', + 'date', + { + type: Sequelize.DataTypes.DATE, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'reviews', + 'author_name', { type: Sequelize.DataTypes.TEXT, @@ -1073,11 +1320,154 @@ module.exports = { + + await queryInterface.addColumn( + 'reviews', + 'rating', + { + type: Sequelize.DataTypes.INTEGER, + + + + }, + { transaction } + ); + await queryInterface.addColumn( - 'product_variants', + 'reviews', + 'comment', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'reviews', + 'created', + { + type: Sequelize.DataTypes.DATE, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'reviews', + 'approved', + { + type: Sequelize.DataTypes.BOOLEAN, + + defaultValue: false, + allowNull: false, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'reviews', + 'productId', + { + type: Sequelize.DataTypes.UUID, + + + + references: { + model: 'products', + key: 'id', + }, + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'discounts', + 'code', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'discounts', + 'description', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'discounts', + 'type', + { + type: Sequelize.DataTypes.ENUM, + + + values: ['Percentage','Fixed'], + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'discounts', + 'value', + { + type: Sequelize.DataTypes.DECIMAL, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'discounts', 'active', { type: Sequelize.DataTypes.BOOLEAN, @@ -1095,30 +1485,10 @@ module.exports = { await queryInterface.addColumn( - 'inventory', - 'variantId', + 'discounts', + 'starts', { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'product_variants', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'inventory', - 'quantity', - { - type: Sequelize.DataTypes.INTEGER, + type: Sequelize.DataTypes.DATE, @@ -1130,42 +1500,10 @@ module.exports = { await queryInterface.addColumn( - 'inventory', - 'location', + 'discounts', + 'ends', { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'inventory', - 'change_type', - { - type: Sequelize.DataTypes.ENUM, - - - values: ['addition','deduction','adjustment','transfer'], - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'inventory', - 'note', - { - type: Sequelize.DataTypes.TEXT, + type: Sequelize.DataTypes.DATE, @@ -1230,7 +1568,37 @@ module.exports = { await queryInterface.addColumn( 'orders', - 'total', + 'order_date', + { + type: Sequelize.DataTypes.DATE, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'orders', + 'delivery_date', + { + type: Sequelize.DataTypes.DATE, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'orders', + 'subtotal', { type: Sequelize.DataTypes.DECIMAL, @@ -1245,9 +1613,9 @@ module.exports = { await queryInterface.addColumn( 'orders', - 'placed_at', + 'tax', { - type: Sequelize.DataTypes.DATE, + type: Sequelize.DataTypes.DECIMAL, @@ -1260,9 +1628,9 @@ module.exports = { await queryInterface.addColumn( 'orders', - 'shipped_at', + 'shipping_fee', { - type: Sequelize.DataTypes.DATE, + type: Sequelize.DataTypes.DECIMAL, @@ -1275,13 +1643,11 @@ module.exports = { await queryInterface.addColumn( 'orders', - 'payment_status', + 'total', { - type: Sequelize.DataTypes.ENUM, + type: Sequelize.DataTypes.DECIMAL, - values: ['Paid','Unpaid','PartiallyRefunded','Refunded'], - }, { transaction } @@ -1305,9 +1671,50 @@ module.exports = { + await queryInterface.addColumn( + 'orders', + 'billing_address', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + + + + + + + await queryInterface.addColumn( + 'orders', + 'assigned_toId', + { + type: Sequelize.DataTypes.UUID, + + + + references: { + model: 'users', + key: 'id', + }, + + }, + { transaction } + ); + + + + await queryInterface.addColumn( 'order_items', - 'name', + 'product_name', { type: Sequelize.DataTypes.TEXT, @@ -1322,34 +1729,14 @@ module.exports = { await queryInterface.addColumn( 'order_items', - 'orderId', + 'productId', { type: Sequelize.DataTypes.UUID, references: { - model: 'orders', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'order_items', - 'product_variantId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'product_variants', + model: 'products', key: 'id', }, @@ -1406,28 +1793,8 @@ module.exports = { await queryInterface.addColumn( - 'payments', - 'orderId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'orders', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'payments', - 'transaction_id', + 'order_items', + 'sku', { type: Sequelize.DataTypes.TEXT, @@ -1442,13 +1809,11 @@ module.exports = { await queryInterface.addColumn( 'payments', - 'payment_method', + 'transaction_ref', { - type: Sequelize.DataTypes.ENUM, + type: Sequelize.DataTypes.TEXT, - values: ['CreditCard','PayPal','BankTransfer','Cash'], - }, { transaction } @@ -1472,6 +1837,23 @@ module.exports = { + await queryInterface.addColumn( + 'payments', + 'method', + { + type: Sequelize.DataTypes.ENUM, + + + values: ['CreditCard','PayPal','BankTransfer','Cash'], + + + }, + { transaction } + ); + + + + await queryInterface.addColumn( 'payments', 'status', @@ -1479,7 +1861,7 @@ module.exports = { type: Sequelize.DataTypes.ENUM, - values: ['Succeeded','Failed','Pending','Refunded'], + values: ['Pending','Completed','Failed','Refunded'], }, @@ -1505,30 +1887,30 @@ module.exports = { await queryInterface.addColumn( - 'shipments', - 'orderId', + 'payments', + 'provider', { - type: Sequelize.DataTypes.UUID, + type: Sequelize.DataTypes.TEXT, - references: { - model: 'orders', - key: 'id', - }, - }, { transaction } ); + + await queryInterface.addColumn( - 'shipments', - 'carrier', + 'payments', + 'refunded', { - type: Sequelize.DataTypes.TEXT, + type: Sequelize.DataTypes.BOOLEAN, + + defaultValue: false, + allowNull: false, @@ -1554,6 +1936,21 @@ module.exports = { + await queryInterface.addColumn( + 'shipments', + 'carrier', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + await queryInterface.addColumn( 'shipments', 'status', @@ -1561,7 +1958,7 @@ module.exports = { type: Sequelize.DataTypes.ENUM, - values: ['Pending','InTransit','Delivered','Returned'], + values: ['LabelCreated','InTransit','Delivered','Returned'], }, @@ -1603,96 +2000,6 @@ module.exports = { await queryInterface.addColumn( 'shipments', - 'cost', - { - type: Sequelize.DataTypes.DECIMAL, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'suppliers', - 'name', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'suppliers', - 'contact_name', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'suppliers', - 'phone', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'suppliers', - 'email', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'suppliers', - 'address', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'suppliers', 'notes', { type: Sequelize.DataTypes.TEXT, @@ -1704,6 +2011,118 @@ module.exports = { ); + + + await queryInterface.addColumn( + 'returns', + 'rma_number', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'returns', + 'orderId', + { + type: Sequelize.DataTypes.UUID, + + + + references: { + model: 'orders', + key: 'id', + }, + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'returns', + 'reason', + { + type: Sequelize.DataTypes.TEXT, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'returns', + 'status', + { + type: Sequelize.DataTypes.ENUM, + + + values: ['Requested','Approved','Rejected','Completed'], + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'returns', + 'requested_at', + { + type: Sequelize.DataTypes.DATE, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'returns', + 'processed_at', + { + type: Sequelize.DataTypes.DATE, + + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'returns', + 'refund_amount', + { + type: Sequelize.DataTypes.DECIMAL, + + + + }, + { transaction } + ); + + await transaction.commit(); @@ -1726,48 +2145,56 @@ module.exports = { await queryInterface.removeColumn( - 'suppliers', - 'notes', + 'returns', + 'refund_amount', { transaction } ); await queryInterface.removeColumn( - 'suppliers', - 'address', + 'returns', + 'processed_at', { transaction } ); await queryInterface.removeColumn( - 'suppliers', - 'email', + 'returns', + 'requested_at', { transaction } ); await queryInterface.removeColumn( - 'suppliers', - 'phone', + 'returns', + 'status', { transaction } ); await queryInterface.removeColumn( - 'suppliers', - 'contact_name', + 'returns', + 'reason', { transaction } ); await queryInterface.removeColumn( - 'suppliers', - 'name', + 'returns', + 'orderId', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'returns', + 'rma_number', { transaction } ); @@ -1775,7 +2202,7 @@ module.exports = { await queryInterface.removeColumn( 'shipments', - 'cost', + 'notes', { transaction } ); @@ -1805,14 +2232,6 @@ module.exports = { - await queryInterface.removeColumn( - 'shipments', - 'tracking_number', - { transaction } - ); - - - await queryInterface.removeColumn( 'shipments', 'carrier', @@ -1823,7 +2242,25 @@ module.exports = { await queryInterface.removeColumn( 'shipments', - 'orderId', + 'tracking_number', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'payments', + 'refunded', + { transaction } + ); + + + + + + await queryInterface.removeColumn( + 'payments', + 'provider', { transaction } ); @@ -1845,6 +2282,14 @@ module.exports = { + await queryInterface.removeColumn( + 'payments', + 'method', + { transaction } + ); + + + await queryInterface.removeColumn( 'payments', 'amount', @@ -1855,23 +2300,15 @@ module.exports = { await queryInterface.removeColumn( 'payments', - 'payment_method', + 'transaction_ref', { transaction } ); await queryInterface.removeColumn( - 'payments', - 'transaction_id', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'payments', - 'orderId', + 'order_items', + 'sku', { transaction } ); @@ -1903,7 +2340,7 @@ module.exports = { await queryInterface.removeColumn( 'order_items', - 'product_variantId', + 'productId', { transaction } ); @@ -1911,15 +2348,29 @@ module.exports = { await queryInterface.removeColumn( 'order_items', - 'orderId', + 'product_name', { transaction } ); await queryInterface.removeColumn( - 'order_items', - 'name', + 'orders', + 'assigned_toId', + { transaction } + ); + + + + + + + + + + await queryInterface.removeColumn( + 'orders', + 'billing_address', { transaction } ); @@ -1933,30 +2384,6 @@ module.exports = { - await queryInterface.removeColumn( - 'orders', - 'payment_status', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'orders', - 'shipped_at', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'orders', - 'placed_at', - { transaction } - ); - - - await queryInterface.removeColumn( 'orders', 'total', @@ -1965,6 +2392,46 @@ module.exports = { + await queryInterface.removeColumn( + 'orders', + 'shipping_fee', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'orders', + 'tax', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'orders', + 'subtotal', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'orders', + 'delivery_date', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'orders', + 'order_date', + { transaction } + ); + + + await queryInterface.removeColumn( 'orders', 'status', @@ -1990,89 +2457,63 @@ module.exports = { await queryInterface.removeColumn( - 'inventory', - 'note', + 'discounts', + 'ends', { transaction } ); await queryInterface.removeColumn( - 'inventory', - 'change_type', + 'discounts', + 'starts', { transaction } ); await queryInterface.removeColumn( - 'inventory', - 'location', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'inventory', - 'quantity', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'inventory', - 'variantId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'product_variants', + 'discounts', 'active', { transaction } ); - - await queryInterface.removeColumn( - 'product_variants', - 'attributes', + 'discounts', + 'value', { transaction } ); await queryInterface.removeColumn( - 'product_variants', - 'stock', + 'discounts', + 'type', { transaction } ); await queryInterface.removeColumn( - 'product_variants', - 'price', + 'discounts', + 'description', { transaction } ); await queryInterface.removeColumn( - 'product_variants', - 'supplierId', + 'discounts', + 'code', { transaction } ); await queryInterface.removeColumn( - 'product_variants', + 'reviews', 'productId', { transaction } ); @@ -2080,16 +2521,90 @@ module.exports = { await queryInterface.removeColumn( - 'product_variants', - 'title', + 'reviews', + 'approved', { transaction } ); await queryInterface.removeColumn( - 'product_variants', - 'sku', + 'reviews', + 'created', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'reviews', + 'comment', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'reviews', + 'rating', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'reviews', + 'author_name', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'inventory_movements', + 'date', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'inventory_movements', + 'reason', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'inventory_movements', + 'change', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'inventory_movements', + 'productId', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'inventory_movements', + 'note', + { transaction } + ); + + + + + + await queryInterface.removeColumn( + 'products', + 'supplierId', { transaction } ); @@ -2115,7 +2630,23 @@ module.exports = { await queryInterface.removeColumn( 'products', - 'weight', + 'stock', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'products', + 'currency', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'products', + 'cost', { transaction } ); @@ -2153,6 +2684,70 @@ module.exports = { + await queryInterface.removeColumn( + 'tags', + 'color', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'tags', + 'name', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'suppliers', + 'notes', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'suppliers', + 'address', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'suppliers', + 'phone', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'suppliers', + 'email', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'suppliers', + 'contact_name', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'suppliers', + 'name', + { transaction } + ); + + + await queryInterface.removeColumn( 'categories', 'parentId', @@ -2179,7 +2774,7 @@ module.exports = { await queryInterface.removeColumn( 'customers', - 'vip', + 'loyalty_points', { transaction } ); @@ -2195,7 +2790,7 @@ module.exports = { await queryInterface.removeColumn( 'customers', - 'shipping_address', + 'address', { transaction } ); @@ -2203,7 +2798,7 @@ module.exports = { await queryInterface.removeColumn( 'customers', - 'billing_address', + 'company', { transaction } ); @@ -2367,7 +2962,7 @@ module.exports = { - await queryInterface.dropTable('suppliers', { transaction }); + await queryInterface.dropTable('returns', { transaction }); @@ -2387,11 +2982,15 @@ module.exports = { - await queryInterface.dropTable('inventory', { transaction }); + await queryInterface.dropTable('discounts', { transaction }); - await queryInterface.dropTable('product_variants', { transaction }); + await queryInterface.dropTable('reviews', { transaction }); + + + + await queryInterface.dropTable('inventory_movements', { transaction }); @@ -2399,6 +2998,14 @@ module.exports = { + await queryInterface.dropTable('tags', { transaction }); + + + + await queryInterface.dropTable('suppliers', { transaction }); + + + await queryInterface.dropTable('categories', { transaction }); diff --git a/backend/src/db/models/categories.js b/backend/src/db/models/categories.js index 6fbc365..c2b7447 100644 --- a/backend/src/db/models/categories.js +++ b/backend/src/db/models/categories.js @@ -52,6 +52,8 @@ description: { + + db.categories.hasMany(db.products, { as: 'products_category', foreignKey: { @@ -69,6 +71,7 @@ description: { + //end loop diff --git a/backend/src/db/models/customers.js b/backend/src/db/models/customers.js index 8c71601..85d326e 100644 --- a/backend/src/db/models/customers.js +++ b/backend/src/db/models/customers.js @@ -35,14 +35,14 @@ phone: { }, -billing_address: { +company: { type: DataTypes.TEXT, }, -shipping_address: { +address: { type: DataTypes.TEXT, @@ -56,11 +56,8 @@ notes: { }, -vip: { - type: DataTypes.BOOLEAN, - - allowNull: false, - defaultValue: false, +loyalty_points: { + type: DataTypes.INTEGER, @@ -93,6 +90,9 @@ vip: { + + + db.customers.hasMany(db.orders, { as: 'orders_customer', foreignKey: { diff --git a/backend/src/db/models/discounts.js b/backend/src/db/models/discounts.js new file mode 100644 index 0000000..f20de97 --- /dev/null +++ b/backend/src/db/models/discounts.js @@ -0,0 +1,135 @@ +const config = require('../../config'); +const providers = config.providers; +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const moment = require('moment'); + +module.exports = function(sequelize, DataTypes) { + const discounts = sequelize.define( + 'discounts', + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + +code: { + type: DataTypes.TEXT, + + + + }, + +description: { + type: DataTypes.TEXT, + + + + }, + +type: { + type: DataTypes.ENUM, + + + + values: [ + +"Percentage", + + +"Fixed" + + ], + + }, + +value: { + type: DataTypes.DECIMAL, + + + + }, + +active: { + type: DataTypes.BOOLEAN, + + allowNull: false, + defaultValue: false, + + + + }, + +starts: { + type: DataTypes.DATE, + + + + }, + +ends: { + type: DataTypes.DATE, + + + + }, + + importHash: { + type: DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { + timestamps: true, + paranoid: true, + freezeTableName: true, + }, + ); + + discounts.associate = (db) => { + + +/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity + + + + + + + + + + + + + + + + + + + +//end loop + + + + + + + db.discounts.belongsTo(db.users, { + as: 'createdBy', + }); + + db.discounts.belongsTo(db.users, { + as: 'updatedBy', + }); + }; + + + + return discounts; +}; + + diff --git a/backend/src/db/models/inventory.js b/backend/src/db/models/inventory_movements.js similarity index 70% rename from backend/src/db/models/inventory.js rename to backend/src/db/models/inventory_movements.js index cf15449..1ec3ff7 100644 --- a/backend/src/db/models/inventory.js +++ b/backend/src/db/models/inventory_movements.js @@ -5,8 +5,8 @@ const bcrypt = require('bcrypt'); const moment = require('moment'); module.exports = function(sequelize, DataTypes) { - const inventory = sequelize.define( - 'inventory', + const inventory_movements = sequelize.define( + 'inventory_movements', { id: { type: DataTypes.UUID, @@ -14,44 +14,44 @@ module.exports = function(sequelize, DataTypes) { primaryKey: true, }, -quantity: { +note: { + type: DataTypes.TEXT, + + + + }, + +change: { type: DataTypes.INTEGER, }, -location: { - type: DataTypes.TEXT, - - - - }, - -change_type: { +reason: { type: DataTypes.ENUM, values: [ -"addition", +"Purchase", -"deduction", +"Sale", -"adjustment", +"Return", -"transfer" +"Adjustment" ], }, -note: { - type: DataTypes.TEXT, +date: { + type: DataTypes.DATE, @@ -70,7 +70,7 @@ note: { }, ); - inventory.associate = (db) => { + inventory_movements.associate = (db) => { /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity @@ -90,14 +90,17 @@ note: { + + + //end loop - db.inventory.belongsTo(db.product_variants, { - as: 'variant', + db.inventory_movements.belongsTo(db.products, { + as: 'product', foreignKey: { - name: 'variantId', + name: 'productId', }, constraints: false, }); @@ -105,18 +108,18 @@ note: { - db.inventory.belongsTo(db.users, { + db.inventory_movements.belongsTo(db.users, { as: 'createdBy', }); - db.inventory.belongsTo(db.users, { + db.inventory_movements.belongsTo(db.users, { as: 'updatedBy', }); }; - return inventory; + return inventory_movements; }; diff --git a/backend/src/db/models/order_items.js b/backend/src/db/models/order_items.js index 4f1d1cf..2132e57 100644 --- a/backend/src/db/models/order_items.js +++ b/backend/src/db/models/order_items.js @@ -14,7 +14,7 @@ module.exports = function(sequelize, DataTypes) { primaryKey: true, }, -name: { +product_name: { type: DataTypes.TEXT, @@ -40,6 +40,13 @@ total: { + }, + +sku: { + type: DataTypes.TEXT, + + + }, importHash: { @@ -75,22 +82,17 @@ total: { + + + //end loop - db.order_items.belongsTo(db.orders, { - as: 'order', + db.order_items.belongsTo(db.products, { + as: 'product', foreignKey: { - name: 'orderId', - }, - constraints: false, - }); - - db.order_items.belongsTo(db.product_variants, { - as: 'product_variant', - foreignKey: { - name: 'product_variantId', + name: 'productId', }, constraints: false, }); diff --git a/backend/src/db/models/orders.js b/backend/src/db/models/orders.js index 9d1d899..3c17278 100644 --- a/backend/src/db/models/orders.js +++ b/backend/src/db/models/orders.js @@ -49,6 +49,41 @@ status: { }, +order_date: { + type: DataTypes.DATE, + + + + }, + +delivery_date: { + type: DataTypes.DATE, + + + + }, + +subtotal: { + type: DataTypes.DECIMAL, + + + + }, + +tax: { + type: DataTypes.DECIMAL, + + + + }, + +shipping_fee: { + type: DataTypes.DECIMAL, + + + + }, + total: { type: DataTypes.DECIMAL, @@ -56,47 +91,18 @@ total: { }, -placed_at: { - type: DataTypes.DATE, - - - - }, - -shipped_at: { - type: DataTypes.DATE, - - - - }, - -payment_status: { - type: DataTypes.ENUM, - - - - values: [ - -"Paid", - - -"Unpaid", - - -"PartiallyRefunded", - - -"Refunded" - - ], - - }, - shipping_address: { type: DataTypes.TEXT, + }, + +billing_address: { + type: DataTypes.TEXT, + + + }, importHash: { @@ -114,6 +120,60 @@ shipping_address: { orders.associate = (db) => { + db.orders.belongsToMany(db.order_items, { + as: 'items', + foreignKey: { + name: 'orders_itemsId', + }, + constraints: false, + through: 'ordersItemsOrder_items', + }); + + db.orders.belongsToMany(db.order_items, { + as: 'items_filter', + foreignKey: { + name: 'orders_itemsId', + }, + constraints: false, + through: 'ordersItemsOrder_items', + }); + + db.orders.belongsToMany(db.payments, { + as: 'payments', + foreignKey: { + name: 'orders_paymentsId', + }, + constraints: false, + through: 'ordersPaymentsPayments', + }); + + db.orders.belongsToMany(db.payments, { + as: 'payments_filter', + foreignKey: { + name: 'orders_paymentsId', + }, + constraints: false, + through: 'ordersPaymentsPayments', + }); + + db.orders.belongsToMany(db.shipments, { + as: 'shipments', + foreignKey: { + name: 'orders_shipmentsId', + }, + constraints: false, + through: 'ordersShipmentsShipments', + }); + + db.orders.belongsToMany(db.shipments, { + as: 'shipments_filter', + foreignKey: { + name: 'orders_shipmentsId', + }, + constraints: false, + through: 'ordersShipmentsShipments', + }); + /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity @@ -127,8 +187,14 @@ shipping_address: { - db.orders.hasMany(db.order_items, { - as: 'order_items_order', + + + + + + + db.orders.hasMany(db.returns, { + as: 'returns_order', foreignKey: { name: 'orderId', }, @@ -136,25 +202,6 @@ shipping_address: { }); - db.orders.hasMany(db.payments, { - as: 'payments_order', - foreignKey: { - name: 'orderId', - }, - constraints: false, - }); - - - db.orders.hasMany(db.shipments, { - as: 'shipments_order', - foreignKey: { - name: 'orderId', - }, - constraints: false, - }); - - - //end loop @@ -168,6 +215,14 @@ shipping_address: { constraints: false, }); + db.orders.belongsTo(db.users, { + as: 'assigned_to', + foreignKey: { + name: 'assigned_toId', + }, + constraints: false, + }); + diff --git a/backend/src/db/models/payments.js b/backend/src/db/models/payments.js index 8e0bbdc..55b6951 100644 --- a/backend/src/db/models/payments.js +++ b/backend/src/db/models/payments.js @@ -14,14 +14,21 @@ module.exports = function(sequelize, DataTypes) { primaryKey: true, }, -transaction_id: { +transaction_ref: { type: DataTypes.TEXT, }, -payment_method: { +amount: { + type: DataTypes.DECIMAL, + + + + }, + +method: { type: DataTypes.ENUM, @@ -43,13 +50,6 @@ payment_method: { }, -amount: { - type: DataTypes.DECIMAL, - - - - }, - status: { type: DataTypes.ENUM, @@ -57,15 +57,15 @@ status: { values: [ -"Succeeded", +"Pending", + + +"Completed", "Failed", -"Pending", - - "Refunded" ], @@ -77,6 +77,23 @@ paid_at: { + }, + +provider: { + type: DataTypes.TEXT, + + + + }, + +refunded: { + type: DataTypes.BOOLEAN, + + allowNull: false, + defaultValue: false, + + + }, importHash: { @@ -112,21 +129,26 @@ paid_at: { + + + //end loop - db.payments.belongsTo(db.orders, { - as: 'order', - foreignKey: { - name: 'orderId', - }, + + + db.payments.hasMany(db.file, { + as: 'receipt', + foreignKey: 'belongsToId', constraints: false, + scope: { + belongsTo: db.payments.getTableName(), + belongsToColumn: 'receipt', + }, }); - - db.payments.belongsTo(db.users, { as: 'createdBy', }); diff --git a/backend/src/db/models/permissions.js b/backend/src/db/models/permissions.js index 2a3e71a..f1dcd95 100644 --- a/backend/src/db/models/permissions.js +++ b/backend/src/db/models/permissions.js @@ -54,6 +54,9 @@ name: { + + + //end loop diff --git a/backend/src/db/models/products.js b/backend/src/db/models/products.js index cb27060..0ea6de9 100644 --- a/backend/src/db/models/products.js +++ b/backend/src/db/models/products.js @@ -42,11 +42,25 @@ price: { }, -weight: { +cost: { type: DataTypes.DECIMAL, + }, + +currency: { + type: DataTypes.TEXT, + + + + }, + +stock: { + type: DataTypes.INTEGER, + + + }, active: { @@ -74,6 +88,24 @@ active: { products.associate = (db) => { + db.products.belongsToMany(db.tags, { + as: 'tags', + foreignKey: { + name: 'products_tagsId', + }, + constraints: false, + through: 'productsTagsTags', + }); + + db.products.belongsToMany(db.tags, { + as: 'tags_filter', + foreignKey: { + name: 'products_tagsId', + }, + constraints: false, + through: 'productsTagsTags', + }); + /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity @@ -84,8 +116,19 @@ active: { - db.products.hasMany(db.product_variants, { - as: 'product_variants_product', + + + db.products.hasMany(db.inventory_movements, { + as: 'inventory_movements_product', + foreignKey: { + name: 'productId', + }, + constraints: false, + }); + + + db.products.hasMany(db.reviews, { + as: 'reviews_product', foreignKey: { name: 'productId', }, @@ -95,6 +138,14 @@ active: { + db.products.hasMany(db.order_items, { + as: 'order_items_product', + foreignKey: { + name: 'productId', + }, + constraints: false, + }); + @@ -112,6 +163,14 @@ active: { constraints: false, }); + db.products.belongsTo(db.suppliers, { + as: 'supplier', + foreignKey: { + name: 'supplierId', + }, + constraints: false, + }); + db.products.hasMany(db.file, { diff --git a/backend/src/db/models/returns.js b/backend/src/db/models/returns.js new file mode 100644 index 0000000..191aa3f --- /dev/null +++ b/backend/src/db/models/returns.js @@ -0,0 +1,139 @@ +const config = require('../../config'); +const providers = config.providers; +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const moment = require('moment'); + +module.exports = function(sequelize, DataTypes) { + const returns = sequelize.define( + 'returns', + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + +rma_number: { + type: DataTypes.TEXT, + + + + }, + +reason: { + type: DataTypes.TEXT, + + + + }, + +status: { + type: DataTypes.ENUM, + + + + values: [ + +"Requested", + + +"Approved", + + +"Rejected", + + +"Completed" + + ], + + }, + +requested_at: { + type: DataTypes.DATE, + + + + }, + +processed_at: { + type: DataTypes.DATE, + + + + }, + +refund_amount: { + type: DataTypes.DECIMAL, + + + + }, + + importHash: { + type: DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { + timestamps: true, + paranoid: true, + freezeTableName: true, + }, + ); + + returns.associate = (db) => { + + +/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity + + + + + + + + + + + + + + + + + + + +//end loop + + + + db.returns.belongsTo(db.orders, { + as: 'order', + foreignKey: { + name: 'orderId', + }, + constraints: false, + }); + + + + + db.returns.belongsTo(db.users, { + as: 'createdBy', + }); + + db.returns.belongsTo(db.users, { + as: 'updatedBy', + }); + }; + + + + return returns; +}; + + diff --git a/backend/src/db/models/product_variants.js b/backend/src/db/models/reviews.js similarity index 50% rename from backend/src/db/models/product_variants.js rename to backend/src/db/models/reviews.js index 7384d74..b8ef64d 100644 --- a/backend/src/db/models/product_variants.js +++ b/backend/src/db/models/reviews.js @@ -5,8 +5,8 @@ const bcrypt = require('bcrypt'); const moment = require('moment'); module.exports = function(sequelize, DataTypes) { - const product_variants = sequelize.define( - 'product_variants', + const reviews = sequelize.define( + 'reviews', { id: { type: DataTypes.UUID, @@ -14,42 +14,35 @@ module.exports = function(sequelize, DataTypes) { primaryKey: true, }, -sku: { +author_name: { type: DataTypes.TEXT, }, -title: { - type: DataTypes.TEXT, - - - - }, - -price: { - type: DataTypes.DECIMAL, - - - - }, - -stock: { +rating: { type: DataTypes.INTEGER, }, -attributes: { +comment: { type: DataTypes.TEXT, }, -active: { +created: { + type: DataTypes.DATE, + + + + }, + +approved: { type: DataTypes.BOOLEAN, allowNull: false, @@ -72,7 +65,7 @@ active: { }, ); - product_variants.associate = (db) => { + reviews.associate = (db) => { /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity @@ -85,23 +78,10 @@ active: { - db.product_variants.hasMany(db.inventory, { - as: 'inventory_variant', - foreignKey: { - name: 'variantId', - }, - constraints: false, - }); - db.product_variants.hasMany(db.order_items, { - as: 'order_items_product_variant', - foreignKey: { - name: 'product_variantId', - }, - constraints: false, - }); + @@ -112,7 +92,7 @@ active: { - db.product_variants.belongsTo(db.products, { + db.reviews.belongsTo(db.products, { as: 'product', foreignKey: { name: 'productId', @@ -120,39 +100,21 @@ active: { constraints: false, }); - db.product_variants.belongsTo(db.suppliers, { - as: 'supplier', - foreignKey: { - name: 'supplierId', - }, - constraints: false, - }); - db.product_variants.hasMany(db.file, { - as: 'images', - foreignKey: 'belongsToId', - constraints: false, - scope: { - belongsTo: db.product_variants.getTableName(), - belongsToColumn: 'images', - }, - }); - - - db.product_variants.belongsTo(db.users, { + db.reviews.belongsTo(db.users, { as: 'createdBy', }); - db.product_variants.belongsTo(db.users, { + db.reviews.belongsTo(db.users, { as: 'updatedBy', }); }; - return product_variants; + return reviews; }; diff --git a/backend/src/db/models/roles.js b/backend/src/db/models/roles.js index e2137a2..2af4d88 100644 --- a/backend/src/db/models/roles.js +++ b/backend/src/db/models/roles.js @@ -87,6 +87,9 @@ role_customization: { + + + //end loop diff --git a/backend/src/db/models/shipments.js b/backend/src/db/models/shipments.js index 5b76f4d..da8012e 100644 --- a/backend/src/db/models/shipments.js +++ b/backend/src/db/models/shipments.js @@ -14,14 +14,14 @@ module.exports = function(sequelize, DataTypes) { primaryKey: true, }, -carrier: { +tracking_number: { type: DataTypes.TEXT, }, -tracking_number: { +carrier: { type: DataTypes.TEXT, @@ -35,7 +35,7 @@ status: { values: [ -"Pending", +"LabelCreated", "InTransit", @@ -64,8 +64,8 @@ delivered_at: { }, -cost: { - type: DataTypes.DECIMAL, +notes: { + type: DataTypes.TEXT, @@ -104,18 +104,13 @@ cost: { + + + //end loop - db.shipments.belongsTo(db.orders, { - as: 'order', - foreignKey: { - name: 'orderId', - }, - constraints: false, - }); - diff --git a/backend/src/db/models/suppliers.js b/backend/src/db/models/suppliers.js index d9eefde..0d10ece 100644 --- a/backend/src/db/models/suppliers.js +++ b/backend/src/db/models/suppliers.js @@ -28,14 +28,14 @@ contact_name: { }, -phone: { +email: { type: DataTypes.TEXT, }, -email: { +phone: { type: DataTypes.TEXT, @@ -81,8 +81,9 @@ notes: { - db.suppliers.hasMany(db.product_variants, { - as: 'product_variants_supplier', + + db.suppliers.hasMany(db.products, { + as: 'products_supplier', foreignKey: { name: 'supplierId', }, @@ -97,6 +98,8 @@ notes: { + + //end loop diff --git a/backend/src/db/models/tags.js b/backend/src/db/models/tags.js new file mode 100644 index 0000000..0b38127 --- /dev/null +++ b/backend/src/db/models/tags.js @@ -0,0 +1,88 @@ +const config = require('../../config'); +const providers = config.providers; +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const moment = require('moment'); + +module.exports = function(sequelize, DataTypes) { + const tags = sequelize.define( + 'tags', + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + +name: { + type: DataTypes.TEXT, + + + + }, + +color: { + type: DataTypes.TEXT, + + + + }, + + importHash: { + type: DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { + timestamps: true, + paranoid: true, + freezeTableName: true, + }, + ); + + tags.associate = (db) => { + + +/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity + + + + + + + + + + + + + + + + + + + +//end loop + + + + + + + db.tags.belongsTo(db.users, { + as: 'createdBy', + }); + + db.tags.belongsTo(db.users, { + as: 'updatedBy', + }); + }; + + + + return tags; +}; + + diff --git a/backend/src/db/models/users.js b/backend/src/db/models/users.js index 5ca2c8e..b9fc085 100644 --- a/backend/src/db/models/users.js +++ b/backend/src/db/models/users.js @@ -152,6 +152,17 @@ provider: { + db.users.hasMany(db.orders, { + as: 'orders_assigned_to', + foreignKey: { + name: 'assigned_toId', + }, + constraints: false, + }); + + + + diff --git a/backend/src/db/seeders/20200430130760-user-roles.js b/backend/src/db/seeders/20200430130760-user-roles.js index 044d0ef..6943cf6 100644 --- a/backend/src/db/seeders/20200430130760-user-roles.js +++ b/backend/src/db/seeders/20200430130760-user-roles.js @@ -33,7 +33,7 @@ module.exports = { - { id: getId("StoreDirector"), name: "Store Director", createdAt, updatedAt }, + { id: getId("StoreOwner"), name: "Store Owner", createdAt, updatedAt }, { id: getId("OperationsManager"), name: "Operations Manager", createdAt, updatedAt }, @@ -61,7 +61,7 @@ module.exports = { } const entities = [ - "users","roles","permissions","customers","categories","products","product_variants","inventory","orders","order_items","payments","shipments","suppliers",, + "users","roles","permissions","customers","categories","suppliers","tags","products","inventory_movements","reviews","discounts","orders","order_items","payments","shipments","returns",, ]; await queryInterface.bulkInsert("permissions", entities.flatMap(createPermissions)); await queryInterface.bulkInsert("permissions", [{ id: getId(`READ_API_DOCS`), createdAt, updatedAt, name: `READ_API_DOCS` }]); @@ -90,19 +90,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_USERS') }, @@ -128,9 +128,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_USERS') }, + @@ -143,9 +143,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_USERS') }, + @@ -158,9 +158,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('READ_USERS') }, + @@ -185,19 +185,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_CUSTOMERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_CUSTOMERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_CUSTOMERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_CUSTOMERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_CUSTOMERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_CUSTOMERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_CUSTOMERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_CUSTOMERS') }, @@ -218,6 +218,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_CUSTOMERS') }, + @@ -237,6 +239,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('DELETE_CUSTOMERS') }, + @@ -244,9 +248,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_CUSTOMERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_CUSTOMERS') }, + @@ -271,6 +275,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('DELETE_CUSTOMERS') }, + @@ -288,19 +294,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_CATEGORIES') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_CATEGORIES') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_CATEGORIES') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_CATEGORIES') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_CATEGORIES') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_CATEGORIES') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_CATEGORIES') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_CATEGORIES') }, @@ -345,9 +351,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_CATEGORIES') }, - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_CATEGORIES') }, + @@ -360,9 +366,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_CATEGORIES') }, - { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('READ_CATEGORIES') }, + @@ -385,19 +391,213 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_PRODUCTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_SUPPLIERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_PRODUCTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_SUPPLIERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_PRODUCTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_SUPPLIERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_PRODUCTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_SUPPLIERS') }, + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_SUPPLIERS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_SUPPLIERS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_SUPPLIERS') }, + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_SUPPLIERS') }, + + + + + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_SUPPLIERS') }, + + + + + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_SUPPLIERS') }, + + + + + + + + + + + + + + + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_TAGS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_TAGS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_TAGS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_TAGS') }, + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_TAGS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_TAGS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_TAGS') }, + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_TAGS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_TAGS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('UPDATE_TAGS') }, + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_TAGS') }, + + + + + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_TAGS') }, + + + + + + + + + + + + + + + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_PRODUCTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_PRODUCTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_PRODUCTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_PRODUCTS') }, @@ -418,6 +618,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_PRODUCTS') }, + @@ -444,6 +646,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_PRODUCTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_PRODUCTS') }, @@ -452,6 +656,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('DELETE_PRODUCTS') }, + @@ -459,6 +665,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_PRODUCTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('READ_PRODUCTS') }, @@ -484,19 +692,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_INVENTORY_MOVEMENTS') }, @@ -505,18 +713,20 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_INVENTORY_MOVEMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_INVENTORY_MOVEMENTS') }, + @@ -524,12 +734,10 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_PRODUCT_VARIANTS') }, - @@ -541,18 +749,20 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('UPDATE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('UPDATE_INVENTORY_MOVEMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('DELETE_INVENTORY_MOVEMENTS') }, + @@ -560,9 +770,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('READ_PRODUCT_VARIANTS') }, + @@ -585,19 +795,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_REVIEWS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_REVIEWS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_REVIEWS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_REVIEWS') }, @@ -606,15 +816,15 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_REVIEWS') }, - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_REVIEWS') }, - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_REVIEWS') }, @@ -625,9 +835,28 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_REVIEWS') }, + - { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_REVIEWS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('UPDATE_REVIEWS') }, + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_REVIEWS') }, + + @@ -640,15 +869,105 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_REVIEWS') }, - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('READ_REVIEWS') }, - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('UPDATE_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('UPDATE_REVIEWS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('DELETE_REVIEWS') }, + + + + + + + + + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_DISCOUNTS') }, + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_DISCOUNTS') }, + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('UPDATE_DISCOUNTS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('DELETE_DISCOUNTS') }, + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_DISCOUNTS') }, + + + + @@ -659,9 +978,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_DISCOUNTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('READ_INVENTORY') }, + @@ -684,19 +1003,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_ORDERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_ORDERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_ORDERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_ORDERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_ORDERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_ORDERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_ORDERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_ORDERS') }, @@ -717,6 +1036,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_ORDERS') }, + @@ -745,6 +1066,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_ORDERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_ORDERS') }, @@ -787,19 +1110,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_ORDER_ITEMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_ORDER_ITEMS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_ORDER_ITEMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_ORDER_ITEMS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_ORDER_ITEMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_ORDER_ITEMS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_ORDER_ITEMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_ORDER_ITEMS') }, @@ -820,6 +1143,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_ORDER_ITEMS') }, + @@ -848,14 +1173,20 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_ORDER_ITEMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_ORDER_ITEMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('UPDATE_ORDER_ITEMS') }, + + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('DELETE_ORDER_ITEMS') }, + @@ -869,8 +1200,6 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('UPDATE_ORDER_ITEMS') }, - @@ -890,19 +1219,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_PAYMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_PAYMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_PAYMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_PAYMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_PAYMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_PAYMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_PAYMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_PAYMENTS') }, @@ -923,6 +1252,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_PAYMENTS') }, + @@ -947,9 +1278,9 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_PAYMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_PAYMENTS') }, + @@ -968,6 +1299,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('UPDATE_PAYMENTS') }, + @@ -987,19 +1320,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_SHIPMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_SHIPMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_SHIPMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_SHIPMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_SHIPMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_SHIPMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_SHIPMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_SHIPMENTS') }, @@ -1020,6 +1353,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_SHIPMENTS') }, + @@ -1027,6 +1362,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_SHIPMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_SHIPMENTS') }, @@ -1054,6 +1391,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('DELETE_SHIPMENTS') }, + @@ -1086,19 +1425,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_RETURNS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('READ_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('READ_RETURNS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('UPDATE_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('UPDATE_RETURNS') }, - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('DELETE_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('DELETE_RETURNS') }, @@ -1107,18 +1446,20 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_RETURNS') }, - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('READ_RETURNS') }, - { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('UPDATE_RETURNS') }, + { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('DELETE_RETURNS') }, + @@ -1126,9 +1467,11 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('CREATE_RETURNS') }, + - { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("SalesManager"), permissionId: getId('READ_RETURNS') }, @@ -1141,9 +1484,11 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('CREATE_RETURNS') }, + - { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("WarehouseStaff"), permissionId: getId('READ_RETURNS') }, @@ -1156,14 +1501,20 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - - - { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('READ_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('CREATE_RETURNS') }, + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('READ_RETURNS') }, + + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('UPDATE_RETURNS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("CustomerSupport"), permissionId: getId('DELETE_RETURNS') }, + @@ -1176,7 +1527,7 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("StoreDirector"), permissionId: getId('CREATE_SEARCH') }, + { createdAt, updatedAt, roles_permissionsId: getId("StoreOwner"), permissionId: getId('CREATE_SEARCH') }, { createdAt, updatedAt, roles_permissionsId: getId("OperationsManager"), permissionId: getId('CREATE_SEARCH') }, @@ -1214,20 +1565,35 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_CATEGORIES') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_CATEGORIES') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_SUPPLIERS') }, + + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_TAGS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_TAGS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_TAGS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_TAGS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_PRODUCTS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_PRODUCTS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_PRODUCTS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_PRODUCTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_PRODUCT_VARIANTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_PRODUCT_VARIANTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_PRODUCT_VARIANTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_PRODUCT_VARIANTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_INVENTORY_MOVEMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_INVENTORY_MOVEMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_INVENTORY_MOVEMENTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_INVENTORY_MOVEMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_INVENTORY') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_INVENTORY') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_INVENTORY') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_INVENTORY') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_REVIEWS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_REVIEWS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_REVIEWS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_REVIEWS') }, + + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_DISCOUNTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_DISCOUNTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_DISCOUNTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_DISCOUNTS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_ORDERS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_ORDERS') }, @@ -1249,10 +1615,10 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_SHIPMENTS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_SHIPMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_SUPPLIERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_SUPPLIERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_SUPPLIERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_SUPPLIERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_RETURNS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_RETURNS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_RETURNS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_RETURNS') }, @@ -1269,7 +1635,7 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - await queryInterface.sequelize.query(`UPDATE "users" SET "app_roleId"='${getId("StoreDirector")}' WHERE "email"='client@hello.com'`); + await queryInterface.sequelize.query(`UPDATE "users" SET "app_roleId"='${getId("StoreOwner")}' WHERE "email"='client@hello.com'`); await queryInterface.sequelize.query(`UPDATE "users" SET "app_roleId"='${getId("OperationsManager")}' WHERE "email"='john@doe.com'`); diff --git a/backend/src/db/seeders/20231127130745-sample-data.js b/backend/src/db/seeders/20231127130745-sample-data.js index 01f787f..149b5eb 100644 --- a/backend/src/db/seeders/20231127130745-sample-data.js +++ b/backend/src/db/seeders/20231127130745-sample-data.js @@ -32,11 +32,17 @@ const Customers = db.customers; const Categories = db.categories; +const Suppliers = db.suppliers; + +const Tags = db.tags; + const Products = db.products; -const ProductVariants = db.product_variants; +const InventoryMovements = db.inventory_movements; -const Inventory = db.inventory; +const Reviews = db.reviews; + +const Discounts = db.discounts; const Orders = db.orders; @@ -46,7 +52,7 @@ const Payments = db.payments; const Shipments = db.shipments; -const Suppliers = db.suppliers; +const Returns = db.returns; @@ -61,49 +67,49 @@ const CustomersData = [ - "name": "Greenfield Bakery", + "name": "GreenGarden Co", - "email": "orders@greenfieldbakery.com", + "email": "purchasing@greengarden.co", - "phone": "+1-206-555-0101", + "phone": "+1-212-555-0111", - "billing_address": "120 Market Street, Seattle, WA 98101", + "company": "GreenGarden Co", - "shipping_address": "120 Market Street, Seattle, WA 98101", + "address": "120 Market St, Suite 300, New York, NY 10001", - "notes": "Weekly bulk orders on Mondays", + "notes": "Wholesale account, net 30", - "vip": true, + "loyalty_points": 620, @@ -114,49 +120,49 @@ const CustomersData = [ - "name": "Willow Retail Co", + "name": "Helen Park", - "email": "purchasing@willowretail.com", + "email": "helen.park@example.com", - "phone": "+1-212-555-0150", + "phone": "+1-312-555-0122", - "billing_address": "55 Hudson Yards, New York, NY 10001", + "company": "", - "shipping_address": "Warehouse B, 2nd Floor, 55 Hudson Yards, New York, NY 10001", + "address": "45 Oak Lane, Chicago, IL 60611", - "notes": "Large seasonal campaigns", + "notes": "Frequent small orders, loyalty member", - "vip": true, + "loyalty_points": 220, @@ -167,49 +173,102 @@ const CustomersData = [ - "name": "Harbor Cafe", + "name": "Crafts & Co", - "email": "contact@harborcafe.com", + "email": "orders@craftsandco.com", - "phone": "+1-619-555-0123", + "phone": "+1-415-555-0133", - "billing_address": "9 Harbor Drive, San Diego, CA 92101", + "company": "Crafts & Co", - "shipping_address": "9 Harbor Drive, San Diego, CA 92101", + "address": "9 Harbor Road, San Francisco, CA 94107", - "notes": "Prefers expedited shipping", + "notes": "Prefers consolidated shipments", - "vip": true, + "loyalty_points": 480, + + + + }, + + { + + + + + "name": "Daniel Ortiz", + + + + + + + "email": "daniel.ortiz@example.com", + + + + + + + "phone": "+1-617-555-0144", + + + + + + + "company": "", + + + + + + + "address": "200 Beacon St, Boston, MA 02116", + + + + + + + "notes": "High value customer", + + + + + + + "loyalty_points": 1100, @@ -226,14 +285,14 @@ const CategoriesData = [ - "name": "Beverages", + "name": "Home Decor", - "description": "Drinks and beverage related products", + "description": "Decorative items for home interiors", @@ -251,14 +310,14 @@ const CategoriesData = [ - "name": "Bakery", + "name": "Kitchen", - "description": "Fresh baked goods and bakery items", + "description": "Cookware and kitchen accessories", @@ -276,14 +335,14 @@ const CategoriesData = [ - "name": "Packaging", + "name": "Outdoor", - "description": "Boxes, wraps, and packaging supplies", + "description": "Outdoor and garden products", @@ -294,6 +353,299 @@ const CategoriesData = [ + }, + + { + + + + + "name": "Textiles", + + + + + + + "description": "Cushions, throws and linens", + + + + + + + // type code here for "relation_one" field + + + + }, + +]; + + + +const SuppliersData = [ + + { + + + + + "name": "Northfield Supplies", + + + + + + + "contact_name": "Grace North", + + + + + + + "email": "grace@northfieldsupplies.com", + + + + + + + "phone": "+1-503-555-0161", + + + + + + + "address": "22 Supply Way, Portland, OR 97201", + + + + + + + "notes": "Lead times vary by season", + + + + }, + + { + + + + + "name": "Harbor Imports", + + + + + + + "contact_name": "Liam Chen", + + + + + + + "email": "liam@harborimports.com", + + + + + + + "phone": "+1-206-555-0162", + + + + + + + "address": "400 Harbor Blvd, Seattle, WA 98101", + + + + + + + "notes": "Handles custom finishes", + + + + }, + + { + + + + + "name": "Sunrise Goods", + + + + + + + "contact_name": "Marta Silva", + + + + + + + "email": "marta@sunrisegoods.com", + + + + + + + "phone": "+1-305-555-0163", + + + + + + + "address": "88 Coral St, Miami, FL 33131", + + + + + + + "notes": "Small batch artisan supplier", + + + + }, + + { + + + + + "name": "Atlas Manufacturing", + + + + + + + "contact_name": "Ken Ito", + + + + + + + "email": "ken.ito@atlasman.com", + + + + + + + "phone": "+1-214-555-0164", + + + + + + + "address": "7 Industrial Ave, Dallas, TX 75207", + + + + + + + "notes": "Bulk discounts available", + + + + }, + +]; + + + +const TagsData = [ + + { + + + + + "name": "Bestseller", + + + + + + + "color": "#FF8A65", + + + + }, + + { + + + + + "name": "New Arrival", + + + + + + + "color": "#4FC3F7", + + + + }, + + { + + + + + "name": "Eco Friendly", + + + + + + + "color": "#8BC34A", + + + + }, + + { + + + + + "name": "Limited", + + + + + + + "color": "#BA68C8", + + + }, ]; @@ -307,221 +659,42 @@ const ProductsData = [ - "name": "Ethiopian Single Origin Coffee 1kg", + "name": "Ceramic Planter Large", - "sku": "COF-ETH-1KG", + "sku": "PLN-CL-001", - "description": "Medium roast, citrus notes, whole bean", + "description": "Matte white ceramic planter for indoor plants", - "price": 28.5, + "price": 34.99, - "weight": 1.0, + "cost": 12.5, - // type code here for "images" field - - - - - - - "active": true, - - - - - - - // type code here for "relation_one" field - - - - }, - - { - - - - - "name": "Classic Croissant Pack", - - - - - - - "sku": "BAK-CRO-6", - - - - - - - "description": "Pack of 6 freshly baked croissants", - - - - - - - "price": 9.75, - - - - - - - "weight": 0.45, - - - - - - - // type code here for "images" field - - - - - - - "active": true, - - - - - - - // type code here for "relation_one" field - - - - }, - - { - - - - - "name": "Compostable Takeaway Boxes (50 pcs)", - - - - - - - "sku": "PKG-CBOX-50", - - - - - - - "description": "Eco friendly takeaway boxes, 500ml capacity", - - - - - - - "price": 16.0, - - - - - - - "weight": 0.8, - - - - - - - // type code here for "images" field - - - - - - - "active": true, - - - - - - - // type code here for "relation_one" field - - - - }, - -]; - - - -const ProductVariantsData = [ - - { - - - - - "sku": "COF-ETH-1KG-BEAN", - - - - - - - "title": "Whole Bean 1kg", - - - - - - - // type code here for "relation_one" field - - - - - - - // type code here for "relation_one" field - - - - - - - "price": 28.5, + "currency": "USD", @@ -535,13 +708,6 @@ const ProductVariantsData = [ - "attributes": "Whole bean, medium roast", - - - - - - // type code here for "images" field @@ -553,6 +719,27 @@ const ProductVariantsData = [ + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_many" field + + + }, { @@ -560,49 +747,49 @@ const ProductVariantsData = [ - "sku": "BAK-CRO-6-FROST", + "name": "Oak Cutting Board", - "title": "Pack of 6 - Classic", + "sku": "KCH-OCB-010", - // type code here for "relation_one" field + "description": "Hand-finished oak cutting board, oil treated", - // type code here for "relation_one" field + "price": 49.5, - "price": 9.75, + "cost": 18.0, - "stock": 300, + "currency": "USD", - "attributes": "Contains butter, freeze stable", + "stock": 60, @@ -620,6 +807,27 @@ const ProductVariantsData = [ + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_many" field + + + }, { @@ -627,49 +835,49 @@ const ProductVariantsData = [ - "sku": "PKG-CBOX-50-GREEN", + "name": "Outdoor String Lights", - "title": "50 pcs Green Boxes", + "sku": "OUT-SL-005", - // type code here for "relation_one" field + "description": "Weather-resistant LED string lights for patios", - // type code here for "relation_one" field + "price": 29.99, - "price": 16.0, + "cost": 9.0, - "stock": 500, + "currency": "USD", - "attributes": "Compostable, 500ml", + "stock": 200, @@ -687,51 +895,160 @@ const ProductVariantsData = [ + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_many" field + + + + }, + + { + + + + + "name": "Linen Throw Pillow", + + + + + + + "sku": "TXT-LTP-021", + + + + + + + "description": "Soft linen throw pillow with removable cover", + + + + + + + "price": 24.0, + + + + + + + "cost": 7.5, + + + + + + + "currency": "USD", + + + + + + + "stock": 340, + + + + + + + // type code here for "images" field + + + + + + + "active": true, + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_many" field + + + }, ]; -const InventoryData = [ +const InventoryMovementsData = [ { - // type code here for "relation_one" field - - - - - - - "quantity": 120, - - - - - - - "location": "Main Warehouse - A1", - - - - - - - "change_type": "transfer", - - - - - - "note": "Initial stock received from supplier", + + + + // type code here for "relation_one" field + + + + + + + "change": 100, + + + + + + + "reason": "Adjustment", + + + + + + + "date": new Date('2025-09-01T10:15:00Z'), + + + }, { @@ -739,6 +1056,13 @@ const InventoryData = [ + "note": "Customer return inspected and restocked", + + + + + + // type code here for "relation_one" field @@ -746,28 +1070,21 @@ const InventoryData = [ - "quantity": 300, + "change": 1, - "location": "Main Warehouse - B2", + "reason": "Sale", - "change_type": "adjustment", - - - - - - - "note": "Baked fresh delivery", + "date": new Date('2025-11-12T14:20:00Z'), @@ -778,6 +1095,13 @@ const InventoryData = [ + "note": "Sold units deducted at POS", + + + + + + // type code here for "relation_one" field @@ -785,28 +1109,468 @@ const InventoryData = [ - "quantity": 500, + "change": -3, - "location": "Main Warehouse - C1", + "reason": "Adjustment", - "change_type": "addition", + "date": new Date('2025-12-02T18:05:00Z'), + + + + }, + + { + + + + + "note": "Stock adjustment after audit", - "note": "Bulk purchase for store supply", + // type code here for "relation_one" field + + + + + + + "change": -2, + + + + + + + "reason": "Sale", + + + + + + + "date": new Date('2025-12-10T09:00:00Z'), + + + + }, + +]; + + + +const ReviewsData = [ + + { + + + + + "author_name": "S. Miller", + + + + + + + "rating": 5, + + + + + + + "comment": "Beautiful planter, great quality", + + + + + + + "created": new Date('2025-10-05T12:00:00Z'), + + + + + + + "approved": true, + + + + + + + // type code here for "relation_one" field + + + + }, + + { + + + + + "author_name": "J. Alvarez", + + + + + + + "rating": 4, + + + + + + + "comment": "Good board but slightly heavy", + + + + + + + "created": new Date('2025-11-02T09:25:00Z'), + + + + + + + "approved": true, + + + + + + + // type code here for "relation_one" field + + + + }, + + { + + + + + "author_name": "M. Rossi", + + + + + + + "rating": 5, + + + + + + + "comment": "Lights are bright and durable", + + + + + + + "created": new Date('2025-12-01T20:10:00Z'), + + + + + + + "approved": true, + + + + + + + // type code here for "relation_one" field + + + + }, + + { + + + + + "author_name": "N. Patel", + + + + + + + "rating": 3, + + + + + + + "comment": "Pillow is nice but color differs slightly", + + + + + + + "created": new Date('2025-12-07T16:40:00Z'), + + + + + + + "approved": true, + + + + + + + // type code here for "relation_one" field + + + + }, + +]; + + + +const DiscountsData = [ + + { + + + + + "code": "WELCOME10", + + + + + + + "description": "10 percent off first order", + + + + + + + "type": "Percentage", + + + + + + + "value": 10, + + + + + + + "active": true, + + + + + + + "starts": new Date('2025-01-01T00:00:00Z'), + + + + + + + "ends": new Date('2026-01-01T00:00:00Z'), + + + + }, + + { + + + + + "code": "HOLIDAY50", + + + + + + + "description": "50 dollars off orders over 300", + + + + + + + "type": "Percentage", + + + + + + + "value": 50, + + + + + + + "active": false, + + + + + + + "starts": new Date('2024-12-15T00:00:00Z'), + + + + + + + "ends": new Date('2025-01-10T00:00:00Z'), + + + + }, + + { + + + + + "code": "SPRING15", + + + + + + + "description": "Spring sale 15 percent", + + + + + + + "type": "Percentage", + + + + + + + "value": 15, + + + + + + + "active": true, + + + + + + + "starts": new Date('2025-03-01T00:00:00Z'), + + + + + + + "ends": new Date('2025-04-15T00:00:00Z'), + + + + }, + + { + + + + + "code": "BULK20", + + + + + + + "description": "20 percent off bulk purchases", + + + + + + + "type": "Fixed", + + + + + + + "value": 20, + + + + + + + "active": true, + + + + + + + "starts": new Date('2025-06-01T00:00:00Z'), + + + + + + + "ends": new Date('2026-06-01T00:00:00Z'), @@ -823,127 +1587,7 @@ const OrdersData = [ - "order_number": "SO-1001", - - - - - - - // type code here for "relation_one" field - - - - - - - "status": "Cancelled", - - - - - - - "total": 85.25, - - - - - - - "placed_at": new Date('2025-11-03T09:15:00Z'), - - - - - - - "shipped_at": new Date('2025-11-04T14:30:00Z'), - - - - - - - "payment_status": "Unpaid", - - - - - - - "shipping_address": "120 Market Street, Seattle, WA 98101", - - - - }, - - { - - - - - "order_number": "SO-1002", - - - - - - - // type code here for "relation_one" field - - - - - - - "status": "Shipped", - - - - - - - "total": 320.0, - - - - - - - "placed_at": new Date('2025-11-04T08:00:00Z'), - - - - - - - "shipped_at": new Date(Date.now()), - - - - - - - "payment_status": "Refunded", - - - - - - - "shipping_address": "Warehouse B, 2nd Floor, 55 Hudson Yards, New York, NY 10001", - - - - }, - - { - - - - - "order_number": "SO-1003", + "order_number": "ORD-20251201-1001", @@ -964,35 +1608,411 @@ const OrdersData = [ - "total": 12.5, + "order_date": new Date('2025-12-01T11:45:00Z'), - "placed_at": new Date('2025-11-02T11:20:00Z'), + "delivery_date": new Date('2025-12-04T13:30:00Z'), - "shipped_at": new Date('2025-11-02T17:10:00Z'), + "subtotal": 69.98, - "payment_status": "Paid", + "tax": 5.6, - "shipping_address": "9 Harbor Drive, San Diego, CA 92101", + "shipping_fee": 8.5, + + + + + + + "total": 84.08, + + + + + + + "shipping_address": "120 Market St, Suite 300, New York, NY 10001", + + + + + + + "billing_address": "120 Market St, Suite 300, New York, NY 10001", + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_one" field + + + + }, + + { + + + + + "order_number": "ORD-20251202-1002", + + + + + + + // type code here for "relation_one" field + + + + + + + "status": "Shipped", + + + + + + + "order_date": new Date('2025-12-02T09:15:00Z'), + + + + + + + "delivery_date": new Date('2025-12-05T12:00:00Z'), + + + + + + + "subtotal": 49.5, + + + + + + + "tax": 3.96, + + + + + + + "shipping_fee": 6.0, + + + + + + + "total": 59.46, + + + + + + + "shipping_address": "45 Oak Lane, Chicago, IL 60611", + + + + + + + "billing_address": "45 Oak Lane, Chicago, IL 60611", + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_one" field + + + + }, + + { + + + + + "order_number": "ORD-20251205-1003", + + + + + + + // type code here for "relation_one" field + + + + + + + "status": "Returned", + + + + + + + "order_date": new Date('2025-12-05T14:50:00Z'), + + + + + + + "delivery_date": new Date('2025-12-09T00:00:00Z'), + + + + + + + "subtotal": 185.97, + + + + + + + "tax": 14.88, + + + + + + + "shipping_fee": 12.0, + + + + + + + "total": 212.85, + + + + + + + "shipping_address": "9 Harbor Road, San Francisco, CA 94107", + + + + + + + "billing_address": "9 Harbor Road, San Francisco, CA 94107", + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_one" field + + + + }, + + { + + + + + "order_number": "ORD-20251120-1004", + + + + + + + // type code here for "relation_one" field + + + + + + + "status": "Delivered", + + + + + + + "order_date": new Date('2025-11-20T09:55:00Z'), + + + + + + + "delivery_date": new Date('2025-11-22T15:45:00Z'), + + + + + + + "subtotal": 96.0, + + + + + + + "tax": 7.68, + + + + + + + "shipping_fee": 0.0, + + + + + + + "total": 103.68, + + + + + + + "shipping_address": "200 Beacon St, Boston, MA 02116", + + + + + + + "billing_address": "200 Beacon St, Boston, MA 02116", + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_many" field + + + + + + + // type code here for "relation_one" field @@ -1009,14 +2029,7 @@ const OrderItemsData = [ - "name": "Ethiopian Coffee 1kg", - - - - - - - // type code here for "relation_one" field + "product_name": "Ceramic Planter Large", @@ -1037,14 +2050,21 @@ const OrderItemsData = [ - "unit_price": 28.5, + "unit_price": 34.99, - "total": 57.0, + "total": 69.98, + + + + + + + "sku": "PLN-CL-001", @@ -1055,7 +2075,7 @@ const OrderItemsData = [ - "name": "Classic Croissant Pack", + "product_name": "Oak Cutting Board", @@ -1069,6 +2089,45 @@ const OrderItemsData = [ + "quantity": 1, + + + + + + + "unit_price": 49.5, + + + + + + + "total": 49.5, + + + + + + + "sku": "KCH-OCB-010", + + + + }, + + { + + + + + "product_name": "Outdoor String Lights", + + + + + + // type code here for "relation_one" field @@ -1083,14 +2142,21 @@ const OrderItemsData = [ - "unit_price": 9.75, + "unit_price": 29.99, - "total": 29.25, + "total": 89.97, + + + + + + + "sku": "OUT-SL-005", @@ -1101,7 +2167,7 @@ const OrderItemsData = [ - "name": "Compostable Takeaway Boxes", + "product_name": "Linen Throw Pillow", @@ -1115,28 +2181,28 @@ const OrderItemsData = [ - // type code here for "relation_one" field + "quantity": 4, - "quantity": 20, + "unit_price": 24.0, - "unit_price": 16.0, + "total": 96.0, - "total": 320.0, + "sku": "TXT-LTP-021", @@ -1153,28 +2219,81 @@ const PaymentsData = [ - // type code here for "relation_one" field + "transaction_ref": "TXN-20251201-001", - "transaction_id": "TXN-55001", + "amount": 69.98, - "payment_method": "Cash", + "method": "PayPal", - "amount": 85.25, + "status": "Completed", + + + + + + + "paid_at": new Date('2025-12-01T12:05:00Z'), + + + + + + + "provider": "Stripe", + + + + + + + // type code here for "files" field + + + + + + + "refunded": true, + + + + }, + + { + + + + + "transaction_ref": "TXN-20251202-002", + + + + + + + "amount": 49.5, + + + + + + + "method": "BankTransfer", @@ -1188,7 +2307,28 @@ const PaymentsData = [ - "paid_at": new Date('2025-11-03T09:18:00Z'), + "paid_at": new Date('2025-12-02T09:30:00Z'), + + + + + + + "provider": "PayPal", + + + + + + + // type code here for "files" field + + + + + + + "refunded": true, @@ -1199,42 +2339,56 @@ const PaymentsData = [ - // type code here for "relation_one" field + "transaction_ref": "TXN-20251205-003", - "transaction_id": "TXN-55002", + "amount": 185.97, - "payment_method": "PayPal", + "method": "CreditCard", - "amount": 0.0, + "status": "Failed", - "status": "Succeeded", + "paid_at": new Date('2025-12-05T15:00:00Z'), - "paid_at": new Date(Date.now()), + "provider": "Bank", + + + + + + + // type code here for "files" field + + + + + + + "refunded": false, @@ -1245,42 +2399,56 @@ const PaymentsData = [ - // type code here for "relation_one" field + "transaction_ref": "TXN-20251120-004", - "transaction_id": "TXN-55003", + "amount": 96.0, - "payment_method": "PayPal", + "method": "CreditCard", - "amount": 12.5, + "status": "Completed", - "status": "Succeeded", + "paid_at": new Date('2025-11-20T10:10:00Z'), - "paid_at": new Date('2025-11-02T11:25:00Z'), + "provider": "Stripe", + + + + + + + // type code here for "files" field + + + + + + + "refunded": false, @@ -1297,21 +2465,14 @@ const ShipmentsData = [ - // type code here for "relation_one" field + "tracking_number": "TRACK-0001", - "carrier": "FastShip", - - - - - - - "tracking_number": "FS10011234", + "carrier": "UPS", @@ -1325,21 +2486,21 @@ const ShipmentsData = [ - "shipped_at": new Date('2025-11-04T14:30:00Z'), + "shipped_at": new Date('2025-12-02T08:00:00Z'), - "delivered_at": new Date('2025-11-05T10:20:00Z'), + "delivered_at": new Date('2025-12-04T13:30:00Z'), - "cost": 7.5, + "notes": "Left at front desk", @@ -1350,49 +2511,42 @@ const ShipmentsData = [ - // type code here for "relation_one" field + "tracking_number": "TRACK-0002", - "carrier": "GroundLine", + "carrier": "USPS", - "tracking_number": "GL20024567", + "status": "LabelCreated", - "status": "InTransit", + "shipped_at": new Date('2025-12-03T11:20:00Z'), - "shipped_at": new Date(Date.now()), + "delivered_at": new Date('2025-12-06T00:00:00Z'), - "delivered_at": new Date(Date.now()), - - - - - - - "cost": 25.0, + "notes": "Signature required", @@ -1403,49 +2557,88 @@ const ShipmentsData = [ - // type code here for "relation_one" field + "tracking_number": "TRACK-0003", - "carrier": "ExpressCo", + "carrier": "FedEx", - "tracking_number": "EX30039876", + "status": "LabelCreated", - "status": "InTransit", + "shipped_at": new Date('2025-12-05T16:00:00Z'), - "shipped_at": new Date('2025-11-02T17:10:00Z'), + "delivered_at": new Date('2025-12-08T00:00:00Z'), - "delivered_at": new Date(Date.now()), + "notes": "Awaiting pickup", + + + + }, + + { + + + + + "tracking_number": "TRACK-0004", - "cost": 4.0, + "carrier": "DHL", + + + + + + + "status": "Delivered", + + + + + + + "shipped_at": new Date('2025-11-15T09:00:00Z'), + + + + + + + "delivered_at": new Date('2025-11-20T10:00:00Z'), + + + + + + + "notes": "Returned to sender", @@ -1455,49 +2648,56 @@ const ShipmentsData = [ -const SuppliersData = [ +const ReturnsData = [ { - "name": "Highland Coffee Roasters", + "rma_number": "RMA-20251204-01", - "contact_name": "Rajesh Kumar", + // type code here for "relation_one" field - "phone": "+1-503-555-0120", + "reason": "Small crack in planter rim", - "email": "sales@highlandroasters.com", + "status": "Approved", - "address": "88 Roast Avenue, Portland, OR 97209", + "requested_at": new Date('2025-12-04T10:00:00Z'), - "notes": "Primary coffee bean supplier", + "processed_at": new Date('2025-12-06T09:30:00Z'), + + + + + + + "refund_amount": 34.99, @@ -1508,42 +2708,49 @@ const SuppliersData = [ - "name": "Golden Bake Supplies", + "rma_number": "RMA-20251207-02", - "contact_name": "Maria Gomez", + // type code here for "relation_one" field - "phone": "+1-415-555-0130", + "reason": "Color mismatch", - "email": "orders@goldensupplies.com", + "status": "Approved", - "address": "40 Baker Lane, San Francisco, CA 94107", + "requested_at": new Date('2025-12-07T15:20:00Z'), - "notes": "Bakery raw materials and baked goods", + "processed_at": new Date('2025-12-09T11:00:00Z'), + + + + + + + "refund_amount": 24.0, @@ -1554,42 +2761,102 @@ const SuppliersData = [ - "name": "EcoPack Solutions", + "rma_number": "RMA-20251211-03", - "contact_name": "Liam OConnor", + // type code here for "relation_one" field - "phone": "+1-206-555-0140", + "reason": "Defective bulb in lights", - "email": "contact@ecopacksolutions.com", + "status": "Completed", - "address": "10 Green Park, Seattle, WA 98109", + "requested_at": new Date('2025-12-11T09:00:00Z'), - "notes": "Sustainable packaging supplier", + "processed_at": new Date('2025-12-12T00:00:00Z'), + + + + + + + "refund_amount": 29.99, + + + + }, + + { + + + + + "rma_number": "RMA-20251121-04", + + + + + + + // type code here for "relation_one" field + + + + + + + "reason": "Wrong item shipped", + + + + + + + "status": "Approved", + + + + + + + "requested_at": new Date('2025-11-21T08:30:00Z'), + + + + + + + "processed_at": new Date('2025-11-22T14:00:00Z'), + + + + + + + "refund_amount": 0.0, @@ -1709,6 +2976,20 @@ const SuppliersData = [ setParent(relatedParent2); } + const relatedParent3 = await Categories.findOne({ + offset: Math.floor(Math.random() * (await Categories.count())), + }); + const Category3 = await Categories.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Category3?.setParent) + { + await + Category3. + setParent(relatedParent3); + } + } @@ -1728,6 +3009,34 @@ const SuppliersData = [ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1776,61 +3085,18 @@ const SuppliersData = [ setCategory(relatedCategory2); } - } - - - - - - - - - - - - - async function associateProductVariantWithProduct() { - - const relatedProduct0 = await Products.findOne({ - offset: Math.floor(Math.random() * (await Products.count())), + const relatedCategory3 = await Categories.findOne({ + offset: Math.floor(Math.random() * (await Categories.count())), }); - const ProductVariant0 = await ProductVariants.findOne({ + const Product3 = await Products.findOne({ order: [['id', 'ASC']], - offset: 0 + offset: 3 }); - if (ProductVariant0?.setProduct) + if (Product3?.setCategory) { await - ProductVariant0. - setProduct(relatedProduct0); - } - - const relatedProduct1 = await Products.findOne({ - offset: Math.floor(Math.random() * (await Products.count())), - }); - const ProductVariant1 = await ProductVariants.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (ProductVariant1?.setProduct) - { - await - ProductVariant1. - setProduct(relatedProduct1); - } - - const relatedProduct2 = await Products.findOne({ - offset: Math.floor(Math.random() * (await Products.count())), - }); - const ProductVariant2 = await ProductVariants.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (ProductVariant2?.setProduct) - { - await - ProductVariant2. - setProduct(relatedProduct2); + Product3. + setCategory(relatedCategory3); } } @@ -1838,55 +3104,138 @@ const SuppliersData = [ - async function associateProductVariantWithSupplier() { + async function associateProductWithSupplier() { const relatedSupplier0 = await Suppliers.findOne({ offset: Math.floor(Math.random() * (await Suppliers.count())), }); - const ProductVariant0 = await ProductVariants.findOne({ + const Product0 = await Products.findOne({ order: [['id', 'ASC']], offset: 0 }); - if (ProductVariant0?.setSupplier) + if (Product0?.setSupplier) { await - ProductVariant0. + Product0. setSupplier(relatedSupplier0); } const relatedSupplier1 = await Suppliers.findOne({ offset: Math.floor(Math.random() * (await Suppliers.count())), }); - const ProductVariant1 = await ProductVariants.findOne({ + const Product1 = await Products.findOne({ order: [['id', 'ASC']], offset: 1 }); - if (ProductVariant1?.setSupplier) + if (Product1?.setSupplier) { await - ProductVariant1. + Product1. setSupplier(relatedSupplier1); } const relatedSupplier2 = await Suppliers.findOne({ offset: Math.floor(Math.random() * (await Suppliers.count())), }); - const ProductVariant2 = await ProductVariants.findOne({ + const Product2 = await Products.findOne({ order: [['id', 'ASC']], offset: 2 }); - if (ProductVariant2?.setSupplier) + if (Product2?.setSupplier) { await - ProductVariant2. + Product2. setSupplier(relatedSupplier2); } + const relatedSupplier3 = await Suppliers.findOne({ + offset: Math.floor(Math.random() * (await Suppliers.count())), + }); + const Product3 = await Products.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Product3?.setSupplier) + { + await + Product3. + setSupplier(relatedSupplier3); + } + } + // Similar logic for "relation_many" + + + + + + + + + + async function associateInventoryMovementWithProduct() { + + const relatedProduct0 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), + }); + const InventoryMovement0 = await InventoryMovements.findOne({ + order: [['id', 'ASC']], + offset: 0 + }); + if (InventoryMovement0?.setProduct) + { + await + InventoryMovement0. + setProduct(relatedProduct0); + } + + const relatedProduct1 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), + }); + const InventoryMovement1 = await InventoryMovements.findOne({ + order: [['id', 'ASC']], + offset: 1 + }); + if (InventoryMovement1?.setProduct) + { + await + InventoryMovement1. + setProduct(relatedProduct1); + } + + const relatedProduct2 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), + }); + const InventoryMovement2 = await InventoryMovements.findOne({ + order: [['id', 'ASC']], + offset: 2 + }); + if (InventoryMovement2?.setProduct) + { + await + InventoryMovement2. + setProduct(relatedProduct2); + } + + const relatedProduct3 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), + }); + const InventoryMovement3 = await InventoryMovements.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (InventoryMovement3?.setProduct) + { + await + InventoryMovement3. + setProduct(relatedProduct3); + } + + } @@ -1898,56 +3247,90 @@ const SuppliersData = [ + + + + + + + + + + - async function associateInventoryWithVariant() { + async function associateReviewWithProduct() { - const relatedVariant0 = await ProductVariants.findOne({ - offset: Math.floor(Math.random() * (await ProductVariants.count())), + const relatedProduct0 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), }); - const Inventory0 = await Inventory.findOne({ + const Review0 = await Reviews.findOne({ order: [['id', 'ASC']], offset: 0 }); - if (Inventory0?.setVariant) + if (Review0?.setProduct) { await - Inventory0. - setVariant(relatedVariant0); + Review0. + setProduct(relatedProduct0); } - const relatedVariant1 = await ProductVariants.findOne({ - offset: Math.floor(Math.random() * (await ProductVariants.count())), + const relatedProduct1 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), }); - const Inventory1 = await Inventory.findOne({ + const Review1 = await Reviews.findOne({ order: [['id', 'ASC']], offset: 1 }); - if (Inventory1?.setVariant) + if (Review1?.setProduct) { await - Inventory1. - setVariant(relatedVariant1); + Review1. + setProduct(relatedProduct1); } - const relatedVariant2 = await ProductVariants.findOne({ - offset: Math.floor(Math.random() * (await ProductVariants.count())), + const relatedProduct2 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), }); - const Inventory2 = await Inventory.findOne({ + const Review2 = await Reviews.findOne({ order: [['id', 'ASC']], offset: 2 }); - if (Inventory2?.setVariant) + if (Review2?.setProduct) { await - Inventory2. - setVariant(relatedVariant2); + Review2. + setProduct(relatedProduct2); + } + + const relatedProduct3 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), + }); + const Review3 = await Reviews.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Review3?.setProduct) + { + await + Review3. + setProduct(relatedProduct3); } } + + + + + + + + + + @@ -2008,6 +3391,20 @@ const SuppliersData = [ setCustomer(relatedCustomer2); } + const relatedCustomer3 = await Customers.findOne({ + offset: Math.floor(Math.random() * (await Customers.count())), + }); + const Order3 = await Orders.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Order3?.setCustomer) + { + await + Order3. + setCustomer(relatedCustomer3); + } + } @@ -2023,6 +3420,87 @@ const SuppliersData = [ + + + + + + + + // Similar logic for "relation_many" + + + + // Similar logic for "relation_many" + + + + // Similar logic for "relation_many" + + + + + async function associateOrderWithAssigned_to() { + + const relatedAssigned_to0 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Order0 = await Orders.findOne({ + order: [['id', 'ASC']], + offset: 0 + }); + if (Order0?.setAssigned_to) + { + await + Order0. + setAssigned_to(relatedAssigned_to0); + } + + const relatedAssigned_to1 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Order1 = await Orders.findOne({ + order: [['id', 'ASC']], + offset: 1 + }); + if (Order1?.setAssigned_to) + { + await + Order1. + setAssigned_to(relatedAssigned_to1); + } + + const relatedAssigned_to2 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Order2 = await Orders.findOne({ + order: [['id', 'ASC']], + offset: 2 + }); + if (Order2?.setAssigned_to) + { + await + Order2. + setAssigned_to(relatedAssigned_to2); + } + + const relatedAssigned_to3 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Order3 = await Orders.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Order3?.setAssigned_to) + { + await + Order3. + setAssigned_to(relatedAssigned_to3); + } + + } + + @@ -2031,101 +3509,68 @@ const SuppliersData = [ - async function associateOrderItemWithOrder() { + async function associateOrderItemWithProduct() { - const relatedOrder0 = await Orders.findOne({ - offset: Math.floor(Math.random() * (await Orders.count())), + const relatedProduct0 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), }); const OrderItem0 = await OrderItems.findOne({ order: [['id', 'ASC']], offset: 0 }); - if (OrderItem0?.setOrder) + if (OrderItem0?.setProduct) { await OrderItem0. - setOrder(relatedOrder0); + setProduct(relatedProduct0); } - const relatedOrder1 = await Orders.findOne({ - offset: Math.floor(Math.random() * (await Orders.count())), + const relatedProduct1 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), }); const OrderItem1 = await OrderItems.findOne({ order: [['id', 'ASC']], offset: 1 }); - if (OrderItem1?.setOrder) + if (OrderItem1?.setProduct) { await OrderItem1. - setOrder(relatedOrder1); + setProduct(relatedProduct1); } - const relatedOrder2 = await Orders.findOne({ - offset: Math.floor(Math.random() * (await Orders.count())), + const relatedProduct2 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), }); const OrderItem2 = await OrderItems.findOne({ order: [['id', 'ASC']], offset: 2 }); - if (OrderItem2?.setOrder) + if (OrderItem2?.setProduct) { await OrderItem2. - setOrder(relatedOrder2); + setProduct(relatedProduct2); + } + + const relatedProduct3 = await Products.findOne({ + offset: Math.floor(Math.random() * (await Products.count())), + }); + const OrderItem3 = await OrderItems.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (OrderItem3?.setProduct) + { + await + OrderItem3. + setProduct(relatedProduct3); } } - - async function associateOrderItemWithProduct_variant() { - - const relatedProduct_variant0 = await ProductVariants.findOne({ - offset: Math.floor(Math.random() * (await ProductVariants.count())), - }); - const OrderItem0 = await OrderItems.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (OrderItem0?.setProduct_variant) - { - await - OrderItem0. - setProduct_variant(relatedProduct_variant0); - } - - const relatedProduct_variant1 = await ProductVariants.findOne({ - offset: Math.floor(Math.random() * (await ProductVariants.count())), - }); - const OrderItem1 = await OrderItems.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (OrderItem1?.setProduct_variant) - { - await - OrderItem1. - setProduct_variant(relatedProduct_variant1); - } - - const relatedProduct_variant2 = await ProductVariants.findOne({ - offset: Math.floor(Math.random() * (await ProductVariants.count())), - }); - const OrderItem2 = await OrderItems.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (OrderItem2?.setProduct_variant) - { - await - OrderItem2. - setProduct_variant(relatedProduct_variant2); - } - - } - @@ -2138,112 +3583,101 @@ const SuppliersData = [ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - async function associatePaymentWithOrder() { + async function associateReturnWithOrder() { const relatedOrder0 = await Orders.findOne({ offset: Math.floor(Math.random() * (await Orders.count())), }); - const Payment0 = await Payments.findOne({ + const Return0 = await Returns.findOne({ order: [['id', 'ASC']], offset: 0 }); - if (Payment0?.setOrder) + if (Return0?.setOrder) { await - Payment0. + Return0. setOrder(relatedOrder0); } const relatedOrder1 = await Orders.findOne({ offset: Math.floor(Math.random() * (await Orders.count())), }); - const Payment1 = await Payments.findOne({ + const Return1 = await Returns.findOne({ order: [['id', 'ASC']], offset: 1 }); - if (Payment1?.setOrder) + if (Return1?.setOrder) { await - Payment1. + Return1. setOrder(relatedOrder1); } const relatedOrder2 = await Orders.findOne({ offset: Math.floor(Math.random() * (await Orders.count())), }); - const Payment2 = await Payments.findOne({ + const Return2 = await Returns.findOne({ order: [['id', 'ASC']], offset: 2 }); - if (Payment2?.setOrder) + if (Return2?.setOrder) { await - Payment2. + Return2. setOrder(relatedOrder2); } - } - - - - - - - - - - - - - - - - - - - async function associateShipmentWithOrder() { - - const relatedOrder0 = await Orders.findOne({ + const relatedOrder3 = await Orders.findOne({ offset: Math.floor(Math.random() * (await Orders.count())), }); - const Shipment0 = await Shipments.findOne({ + const Return3 = await Returns.findOne({ order: [['id', 'ASC']], - offset: 0 + offset: 3 }); - if (Shipment0?.setOrder) + if (Return3?.setOrder) { await - Shipment0. - setOrder(relatedOrder0); - } - - const relatedOrder1 = await Orders.findOne({ - offset: Math.floor(Math.random() * (await Orders.count())), - }); - const Shipment1 = await Shipments.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Shipment1?.setOrder) - { - await - Shipment1. - setOrder(relatedOrder1); - } - - const relatedOrder2 = await Orders.findOne({ - offset: Math.floor(Math.random() * (await Orders.count())), - }); - const Shipment2 = await Shipments.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Shipment2?.setOrder) - { - await - Shipment2. - setOrder(relatedOrder2); + Return3. + setOrder(relatedOrder3); } } @@ -2258,24 +3692,6 @@ const SuppliersData = [ - - - - - - - - - - - - - - - - - - @@ -2298,17 +3714,32 @@ module.exports = { + await Suppliers.bulkCreate(SuppliersData); + + + + + await Tags.bulkCreate(TagsData); + + + + await Products.bulkCreate(ProductsData); - await ProductVariants.bulkCreate(ProductVariantsData); + await InventoryMovements.bulkCreate(InventoryMovementsData); - await Inventory.bulkCreate(InventoryData); + await Reviews.bulkCreate(ReviewsData); + + + + + await Discounts.bulkCreate(DiscountsData); @@ -2333,7 +3764,7 @@ module.exports = { - await Suppliers.bulkCreate(SuppliersData); + await Returns.bulkCreate(ReturnsData); await Promise.all([ @@ -2418,6 +3849,32 @@ module.exports = { + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2425,25 +3882,23 @@ module.exports = { await associateProductWithCategory(), + + + await associateProductWithSupplier(), + + + + // Similar logic for "relation_many" + + - - - await associateProductVariantWithProduct(), - - - - - await associateProductVariantWithSupplier(), - - - - + await associateInventoryMovementWithProduct(), @@ -2454,10 +3909,29 @@ module.exports = { + + + + + + + + + + - await associateInventoryWithVariant(), + await associateReviewWithProduct(), + + + + + + + + + @@ -2490,6 +3964,29 @@ module.exports = { + + + + + + + + // Similar logic for "relation_many" + + + + // Similar logic for "relation_many" + + + + // Similar logic for "relation_many" + + + + + await associateOrderWithAssigned_to(), + + @@ -2497,28 +3994,7 @@ module.exports = { - await associateOrderItemWithOrder(), - - - - - await associateOrderItemWithProduct_variant(), - - - - - - - - - - - - - - await associatePaymentWithOrder(), - - + await associateOrderItemWithProduct(), @@ -2533,8 +4009,7 @@ module.exports = { - - await associateShipmentWithOrder(), + @@ -2552,6 +4027,26 @@ module.exports = { + + + + + + + + + + + + + + + + + + + + await associateReturnWithOrder(), @@ -2582,13 +4077,22 @@ module.exports = { await queryInterface.bulkDelete('categories', null, {}); + await queryInterface.bulkDelete('suppliers', null, {}); + + + await queryInterface.bulkDelete('tags', null, {}); + + await queryInterface.bulkDelete('products', null, {}); - await queryInterface.bulkDelete('product_variants', null, {}); + await queryInterface.bulkDelete('inventory_movements', null, {}); - await queryInterface.bulkDelete('inventory', null, {}); + await queryInterface.bulkDelete('reviews', null, {}); + + + await queryInterface.bulkDelete('discounts', null, {}); await queryInterface.bulkDelete('orders', null, {}); @@ -2603,7 +4107,7 @@ module.exports = { await queryInterface.bulkDelete('shipments', null, {}); - await queryInterface.bulkDelete('suppliers', null, {}); + await queryInterface.bulkDelete('returns', null, {}); }, diff --git a/backend/src/index.js b/backend/src/index.js index 68f51fa..5408269 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -15,6 +15,7 @@ const authRoutes = require('./routes/auth'); const fileRoutes = require('./routes/file'); const searchRoutes = require('./routes/search'); const pexelsRoutes = require('./routes/pexels'); +const dashboardRoutes = require('./routes/dashboard'); const openaiRoutes = require('./routes/openai'); @@ -30,11 +31,17 @@ const customersRoutes = require('./routes/customers'); const categoriesRoutes = require('./routes/categories'); +const suppliersRoutes = require('./routes/suppliers'); + +const tagsRoutes = require('./routes/tags'); + const productsRoutes = require('./routes/products'); -const product_variantsRoutes = require('./routes/product_variants'); +const inventory_movementsRoutes = require('./routes/inventory_movements'); -const inventoryRoutes = require('./routes/inventory'); +const reviewsRoutes = require('./routes/reviews'); + +const discountsRoutes = require('./routes/discounts'); const ordersRoutes = require('./routes/orders'); @@ -44,7 +51,7 @@ const paymentsRoutes = require('./routes/payments'); const shipmentsRoutes = require('./routes/shipments'); -const suppliersRoutes = require('./routes/suppliers'); +const returnsRoutes = require('./routes/returns'); const getBaseUrl = (url) => { @@ -57,8 +64,8 @@ const options = { openapi: "3.0.0", info: { version: "1.0.0", - title: "StoreOps Manager", - description: "StoreOps Manager Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.", + title: "Store Operations Manager", + description: "Store Operations Manager Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.", }, servers: [ { @@ -101,6 +108,7 @@ app.use(bodyParser.json()); app.use('/api/auth', authRoutes); app.use('/api/file', fileRoutes); app.use('/api/pexels', pexelsRoutes); +app.use('/api/dashboard', passport.authenticate('jwt', {session: false}), dashboardRoutes); app.enable('trust proxy'); @@ -114,11 +122,17 @@ app.use('/api/customers', passport.authenticate('jwt', {session: false}), custom app.use('/api/categories', passport.authenticate('jwt', {session: false}), categoriesRoutes); +app.use('/api/suppliers', passport.authenticate('jwt', {session: false}), suppliersRoutes); + +app.use('/api/tags', passport.authenticate('jwt', {session: false}), tagsRoutes); + app.use('/api/products', passport.authenticate('jwt', {session: false}), productsRoutes); -app.use('/api/product_variants', passport.authenticate('jwt', {session: false}), product_variantsRoutes); +app.use('/api/inventory_movements', passport.authenticate('jwt', {session: false}), inventory_movementsRoutes); -app.use('/api/inventory', passport.authenticate('jwt', {session: false}), inventoryRoutes); +app.use('/api/reviews', passport.authenticate('jwt', {session: false}), reviewsRoutes); + +app.use('/api/discounts', passport.authenticate('jwt', {session: false}), discountsRoutes); app.use('/api/orders', passport.authenticate('jwt', {session: false}), ordersRoutes); @@ -128,7 +142,7 @@ app.use('/api/payments', passport.authenticate('jwt', {session: false}), payment app.use('/api/shipments', passport.authenticate('jwt', {session: false}), shipmentsRoutes); -app.use('/api/suppliers', passport.authenticate('jwt', {session: false}), suppliersRoutes); +app.use('/api/returns', passport.authenticate('jwt', {session: false}), returnsRoutes); app.use( '/api/openai', diff --git a/backend/src/routes/categories.js b/backend/src/routes/categories.js index 593da26..95b2f47 100644 --- a/backend/src/routes/categories.js +++ b/backend/src/routes/categories.js @@ -26,6 +26,12 @@ router.use(checkCrudPermissions('categories')); * type: object * properties: + * name: + * type: string + * default: name + * description: + * type: string + * default: description @@ -285,7 +291,7 @@ router.get('/', wrapAsync(async (req, res) => { req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', + const fields = ['id','name','description', diff --git a/backend/src/routes/customers.js b/backend/src/routes/customers.js index 9538351..27537b0 100644 --- a/backend/src/routes/customers.js +++ b/backend/src/routes/customers.js @@ -26,7 +26,28 @@ router.use(checkCrudPermissions('customers')); * type: object * properties: + * name: + * type: string + * default: name + * email: + * type: string + * default: email + * phone: + * type: string + * default: phone + * company: + * type: string + * default: company + * address: + * type: string + * default: address + * notes: + * type: string + * default: notes + * loyalty_points: + * type: integer + * format: int64 */ @@ -285,8 +306,8 @@ router.get('/', wrapAsync(async (req, res) => { req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', - + const fields = ['id','name','email','phone','company','address','notes', + 'loyalty_points', ]; diff --git a/backend/src/routes/dashboard.js b/backend/src/routes/dashboard.js new file mode 100644 index 0000000..b3825a6 --- /dev/null +++ b/backend/src/routes/dashboard.js @@ -0,0 +1,60 @@ + +const models = require('../db/models'); +const { Op } = require('sequelize'); +const moment = require('moment'); + +module.exports = function (app) { + app.get('/api/dashboard/summary', async (req, res) => { + try { + const totalRevenue = await models.payments.sum('amount', { + where: { status: 'Completed' }, + }); + + const salesToday = await models.payments.sum('amount', { + where: { + status: 'Completed', + createdAt: { + [Op.gte]: moment().startOf('day').toDate(), + }, + }, + }); + + const newCustomers = await models.customers.count({ + where: { + createdAt: { + [Op.gte]: moment().subtract(30, 'days').toDate(), + }, + }, + }); + + const pendingOrders = await models.orders.count({ + where: { + status: 'Pending', + }, + }); + + const recentOrders = await models.orders.findAll({ + limit: 5, + order: [['createdAt', 'DESC']], + include: [ + { + model: models.customers, + as: 'customer', + attributes: ['name'], + }, + ], + }); + + res.json({ + totalRevenue: totalRevenue || 0, + salesToday: salesToday || 0, + newCustomers: newCustomers || 0, + pendingOrders: pendingOrders || 0, + recentOrders, + }); + } catch (error) { + console.error(error); + res.status(500).send({ error: 'Internal server error' }); + } + }); +}; diff --git a/backend/src/routes/product_variants.js b/backend/src/routes/discounts.js similarity index 75% rename from backend/src/routes/product_variants.js rename to backend/src/routes/discounts.js index 3961c19..cdb4662 100644 --- a/backend/src/routes/product_variants.js +++ b/backend/src/routes/discounts.js @@ -1,8 +1,8 @@ const express = require('express'); -const Product_variantsService = require('../services/product_variants'); -const Product_variantsDBApi = require('../db/api/product_variants'); +const DiscountsService = require('../services/discounts'); +const DiscountsDBApi = require('../db/api/discounts'); const wrapAsync = require('../helpers').wrapAsync; @@ -15,36 +15,46 @@ const { checkCrudPermissions, } = require('../middlewares/check-permissions'); -router.use(checkCrudPermissions('product_variants')); +router.use(checkCrudPermissions('discounts')); /** * @swagger * components: * schemas: - * Product_variants: + * Discounts: * type: object * properties: + * code: + * type: string + * default: code + * description: + * type: string + * default: description + * value: + * type: integer + * format: int64 + * */ /** * @swagger * tags: - * name: Product_variants - * description: The Product_variants managing API + * name: Discounts + * description: The Discounts managing API */ /** * @swagger -* /api/product_variants: +* /api/discounts: * post: * security: * - bearerAuth: [] -* tags: [Product_variants] +* tags: [Discounts] * summary: Add new item * description: Add new item * requestBody: @@ -56,14 +66,14 @@ router.use(checkCrudPermissions('product_variants')); * data: * description: Data of the updated item * type: object -* $ref: "#/components/schemas/Product_variants" +* $ref: "#/components/schemas/Discounts" * responses: * 200: * description: The item was successfully added * content: * application/json: * schema: -* $ref: "#/components/schemas/Product_variants" +* $ref: "#/components/schemas/Discounts" * 401: * $ref: "#/components/responses/UnauthorizedError" * 405: @@ -74,7 +84,7 @@ router.use(checkCrudPermissions('product_variants')); router.post('/', wrapAsync(async (req, res) => { const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; const link = new URL(referer); - await Product_variantsService.create(req.body.data, req.currentUser, true, link.host); + await DiscountsService.create(req.body.data, req.currentUser, true, link.host); const payload = true; res.status(200).send(payload); })); @@ -85,7 +95,7 @@ router.post('/', wrapAsync(async (req, res) => { * post: * security: * - bearerAuth: [] - * tags: [Product_variants] + * tags: [Discounts] * summary: Bulk import items * description: Bulk import items * requestBody: @@ -98,14 +108,14 @@ router.post('/', wrapAsync(async (req, res) => { * description: Data of the updated items * type: array * items: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * responses: * 200: * description: The items were successfully imported * content: * application/json: * schema: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * 401: * $ref: "#/components/responses/UnauthorizedError" * 405: @@ -117,18 +127,18 @@ router.post('/', wrapAsync(async (req, res) => { router.post('/bulk-import', wrapAsync(async (req, res) => { const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; const link = new URL(referer); - await Product_variantsService.bulkImport(req, res, true, link.host); + await DiscountsService.bulkImport(req, res, true, link.host); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/product_variants/{id}: + * /api/discounts/{id}: * put: * security: * - bearerAuth: [] - * tags: [Product_variants] + * tags: [Discounts] * summary: Update the data of the selected item * description: Update the data of the selected item * parameters: @@ -151,7 +161,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * data: * description: Data of the updated item * type: object - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * required: * - id * responses: @@ -160,7 +170,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * 400: * description: Invalid ID supplied * 401: @@ -171,18 +181,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * description: Some server error */ router.put('/:id', wrapAsync(async (req, res) => { - await Product_variantsService.update(req.body.data, req.body.id, req.currentUser); + await DiscountsService.update(req.body.data, req.body.id, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/product_variants/{id}: + * /api/discounts/{id}: * delete: * security: * - bearerAuth: [] - * tags: [Product_variants] + * tags: [Discounts] * summary: Delete the selected item * description: Delete the selected item * parameters: @@ -198,7 +208,7 @@ router.put('/:id', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * 400: * description: Invalid ID supplied * 401: @@ -209,18 +219,18 @@ router.put('/:id', wrapAsync(async (req, res) => { * description: Some server error */ router.delete('/:id', wrapAsync(async (req, res) => { - await Product_variantsService.remove(req.params.id, req.currentUser); + await DiscountsService.remove(req.params.id, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/product_variants/deleteByIds: + * /api/discounts/deleteByIds: * post: * security: * - bearerAuth: [] - * tags: [Product_variants] + * tags: [Discounts] * summary: Delete the selected item list * description: Delete the selected item list * requestBody: @@ -238,7 +248,7 @@ router.delete('/:id', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -247,29 +257,29 @@ router.delete('/:id', wrapAsync(async (req, res) => { * description: Some server error */ router.post('/deleteByIds', wrapAsync(async (req, res) => { - await Product_variantsService.deleteByIds(req.body.data, req.currentUser); + await DiscountsService.deleteByIds(req.body.data, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/product_variants: + * /api/discounts: * get: * security: * - bearerAuth: [] - * tags: [Product_variants] - * summary: Get all product_variants - * description: Get all product_variants + * tags: [Discounts] + * summary: Get all discounts + * description: Get all discounts * responses: * 200: - * description: Product_variants list successfully received + * description: Discounts list successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -281,14 +291,14 @@ router.get('/', wrapAsync(async (req, res) => { const filetype = req.query.filetype const currentUser = req.currentUser; - const payload = await Product_variantsDBApi.findAll( + const payload = await DiscountsDBApi.findAll( req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', + const fields = ['id','code','description', - - + 'value', + 'starts','ends', ]; const opts = { fields }; try { @@ -307,22 +317,22 @@ router.get('/', wrapAsync(async (req, res) => { /** * @swagger - * /api/product_variants/count: + * /api/discounts/count: * get: * security: * - bearerAuth: [] - * tags: [Product_variants] - * summary: Count all product_variants - * description: Count all product_variants + * tags: [Discounts] + * summary: Count all discounts + * description: Count all discounts * responses: * 200: - * description: Product_variants count successfully received + * description: Discounts count successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -333,7 +343,7 @@ router.get('/', wrapAsync(async (req, res) => { router.get('/count', wrapAsync(async (req, res) => { const currentUser = req.currentUser; - const payload = await Product_variantsDBApi.findAll( + const payload = await DiscountsDBApi.findAll( req.query, null, { countOnly: true, currentUser } @@ -344,22 +354,22 @@ router.get('/count', wrapAsync(async (req, res) => { /** * @swagger - * /api/product_variants/autocomplete: + * /api/discounts/autocomplete: * get: * security: * - bearerAuth: [] - * tags: [Product_variants] - * summary: Find all product_variants that match search criteria - * description: Find all product_variants that match search criteria + * tags: [Discounts] + * summary: Find all discounts that match search criteria + * description: Find all discounts that match search criteria * responses: * 200: - * description: Product_variants list successfully received + * description: Discounts list successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -369,7 +379,7 @@ router.get('/count', wrapAsync(async (req, res) => { */ router.get('/autocomplete', async (req, res) => { - const payload = await Product_variantsDBApi.findAllAutocomplete( + const payload = await DiscountsDBApi.findAllAutocomplete( req.query.query, req.query.limit, req.query.offset, @@ -381,11 +391,11 @@ router.get('/autocomplete', async (req, res) => { /** * @swagger - * /api/product_variants/{id}: + * /api/discounts/{id}: * get: * security: * - bearerAuth: [] - * tags: [Product_variants] + * tags: [Discounts] * summary: Get selected item * description: Get selected item * parameters: @@ -401,7 +411,7 @@ router.get('/autocomplete', async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Product_variants" + * $ref: "#/components/schemas/Discounts" * 400: * description: Invalid ID supplied * 401: @@ -412,7 +422,7 @@ router.get('/autocomplete', async (req, res) => { * description: Some server error */ router.get('/:id', wrapAsync(async (req, res) => { - const payload = await Product_variantsDBApi.findBy( + const payload = await DiscountsDBApi.findBy( { id: req.params.id }, ); diff --git a/backend/src/routes/inventory_movements.js b/backend/src/routes/inventory_movements.js new file mode 100644 index 0000000..c3297eb --- /dev/null +++ b/backend/src/routes/inventory_movements.js @@ -0,0 +1,433 @@ + +const express = require('express'); + +const Inventory_movementsService = require('../services/inventory_movements'); +const Inventory_movementsDBApi = require('../db/api/inventory_movements'); +const wrapAsync = require('../helpers').wrapAsync; + + +const router = express.Router(); + +const { parse } = require('json2csv'); + + +const { + checkCrudPermissions, +} = require('../middlewares/check-permissions'); + +router.use(checkCrudPermissions('inventory_movements')); + + +/** + * @swagger + * components: + * schemas: + * Inventory_movements: + * type: object + * properties: + + * note: + * type: string + * default: note + + * change: + * type: integer + * format: int64 + + + * + */ + +/** + * @swagger + * tags: + * name: Inventory_movements + * description: The Inventory_movements managing API + */ + +/** +* @swagger +* /api/inventory_movements: +* post: +* security: +* - bearerAuth: [] +* tags: [Inventory_movements] +* summary: Add new item +* description: Add new item +* requestBody: +* required: true +* content: +* application/json: +* schema: +* properties: +* data: +* description: Data of the updated item +* type: object +* $ref: "#/components/schemas/Inventory_movements" +* responses: +* 200: +* description: The item was successfully added +* content: +* application/json: +* schema: +* $ref: "#/components/schemas/Inventory_movements" +* 401: +* $ref: "#/components/responses/UnauthorizedError" +* 405: +* description: Invalid input data +* 500: +* description: Some server error +*/ +router.post('/', wrapAsync(async (req, res) => { + const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await Inventory_movementsService.create(req.body.data, req.currentUser, true, link.host); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/budgets/bulk-import: + * post: + * security: + * - bearerAuth: [] + * tags: [Inventory_movements] + * summary: Bulk import items + * description: Bulk import items + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated items + * type: array + * items: + * $ref: "#/components/schemas/Inventory_movements" + * responses: + * 200: + * description: The items were successfully imported + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Inventory_movements" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + * + */ +router.post('/bulk-import', wrapAsync(async (req, res) => { + const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await Inventory_movementsService.bulkImport(req, res, true, link.host); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/inventory_movements/{id}: + * put: + * security: + * - bearerAuth: [] + * tags: [Inventory_movements] + * summary: Update the data of the selected item + * description: Update the data of the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to update + * required: true + * schema: + * type: string + * requestBody: + * description: Set new item data + * required: true + * content: + * application/json: + * schema: + * properties: + * id: + * description: ID of the updated item + * type: string + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Inventory_movements" + * required: + * - id + * responses: + * 200: + * description: The item data was successfully updated + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Inventory_movements" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.put('/:id', wrapAsync(async (req, res) => { + await Inventory_movementsService.update(req.body.data, req.body.id, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/inventory_movements/{id}: + * delete: + * security: + * - bearerAuth: [] + * tags: [Inventory_movements] + * summary: Delete the selected item + * description: Delete the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to delete + * required: true + * schema: + * type: string + * responses: + * 200: + * description: The item was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Inventory_movements" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.delete('/:id', wrapAsync(async (req, res) => { + await Inventory_movementsService.remove(req.params.id, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/inventory_movements/deleteByIds: + * post: + * security: + * - bearerAuth: [] + * tags: [Inventory_movements] + * summary: Delete the selected item list + * description: Delete the selected item list + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * ids: + * description: IDs of the updated items + * type: array + * responses: + * 200: + * description: The items was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Inventory_movements" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Items not found + * 500: + * description: Some server error + */ +router.post('/deleteByIds', wrapAsync(async (req, res) => { + await Inventory_movementsService.deleteByIds(req.body.data, req.currentUser); + const payload = true; + res.status(200).send(payload); + })); + +/** + * @swagger + * /api/inventory_movements: + * get: + * security: + * - bearerAuth: [] + * tags: [Inventory_movements] + * summary: Get all inventory_movements + * description: Get all inventory_movements + * responses: + * 200: + * description: Inventory_movements list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Inventory_movements" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error +*/ +router.get('/', wrapAsync(async (req, res) => { + const filetype = req.query.filetype + + const currentUser = req.currentUser; + const payload = await Inventory_movementsDBApi.findAll( + req.query, { currentUser } + ); + if (filetype && filetype === 'csv') { + const fields = ['id','note', + 'change', + + 'date', + ]; + const opts = { fields }; + try { + const csv = parse(payload.rows, opts); + res.status(200).attachment(csv); + res.send(csv) + + } catch (err) { + console.error(err); + } + } else { + res.status(200).send(payload); + } + +})); + +/** + * @swagger + * /api/inventory_movements/count: + * get: + * security: + * - bearerAuth: [] + * tags: [Inventory_movements] + * summary: Count all inventory_movements + * description: Count all inventory_movements + * responses: + * 200: + * description: Inventory_movements count successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Inventory_movements" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/count', wrapAsync(async (req, res) => { + + const currentUser = req.currentUser; + const payload = await Inventory_movementsDBApi.findAll( + req.query, + null, + { countOnly: true, currentUser } + ); + + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/inventory_movements/autocomplete: + * get: + * security: + * - bearerAuth: [] + * tags: [Inventory_movements] + * summary: Find all inventory_movements that match search criteria + * description: Find all inventory_movements that match search criteria + * responses: + * 200: + * description: Inventory_movements list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Inventory_movements" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/autocomplete', async (req, res) => { + + const payload = await Inventory_movementsDBApi.findAllAutocomplete( + req.query.query, + req.query.limit, + req.query.offset, + + ); + + res.status(200).send(payload); +}); + +/** + * @swagger + * /api/inventory_movements/{id}: + * get: + * security: + * - bearerAuth: [] + * tags: [Inventory_movements] + * summary: Get selected item + * description: Get selected item + * parameters: + * - in: path + * name: id + * description: ID of item to get + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Selected item successfully received + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Inventory_movements" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.get('/:id', wrapAsync(async (req, res) => { + const payload = await Inventory_movementsDBApi.findBy( + { id: req.params.id }, + ); + + + + res.status(200).send(payload); +})); + +router.use('/', require('../helpers').commonErrorHandler); + +module.exports = router; diff --git a/backend/src/routes/order_items.js b/backend/src/routes/order_items.js index ed262ba..4da112b 100644 --- a/backend/src/routes/order_items.js +++ b/backend/src/routes/order_items.js @@ -26,8 +26,23 @@ router.use(checkCrudPermissions('order_items')); * type: object * properties: + * product_name: + * type: string + * default: product_name + * sku: + * type: string + * default: sku + * quantity: + * type: integer + * format: int64 + * unit_price: + * type: integer + * format: int64 + * total: + * type: integer + * format: int64 */ @@ -285,9 +300,9 @@ router.get('/', wrapAsync(async (req, res) => { req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', - - + const fields = ['id','product_name','sku', + 'quantity', + 'unit_price','total', ]; const opts = { fields }; diff --git a/backend/src/routes/orders.js b/backend/src/routes/orders.js index 5cb8864..7db26b5 100644 --- a/backend/src/routes/orders.js +++ b/backend/src/routes/orders.js @@ -26,9 +26,31 @@ router.use(checkCrudPermissions('orders')); * type: object * properties: + * order_number: + * type: string + * default: order_number + * shipping_address: + * type: string + * default: shipping_address + * billing_address: + * type: string + * default: billing_address + * subtotal: + * type: integer + * format: int64 + * tax: + * type: integer + * format: int64 + * shipping_fee: + * type: integer + * format: int64 + * total: + * type: integer + * format: int64 + * */ /** @@ -285,10 +307,10 @@ router.get('/', wrapAsync(async (req, res) => { req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', + const fields = ['id','order_number','shipping_address','billing_address', - - + 'subtotal','tax','shipping_fee','total', + 'order_date','delivery_date', ]; const opts = { fields }; try { diff --git a/backend/src/routes/payments.js b/backend/src/routes/payments.js index 465939b..5069411 100644 --- a/backend/src/routes/payments.js +++ b/backend/src/routes/payments.js @@ -26,9 +26,20 @@ router.use(checkCrudPermissions('payments')); * type: object * properties: + * transaction_ref: + * type: string + * default: transaction_ref + * provider: + * type: string + * default: provider + * amount: + * type: integer + * format: int64 + * + * */ /** @@ -285,10 +296,10 @@ router.get('/', wrapAsync(async (req, res) => { req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', + const fields = ['id','transaction_ref','provider', - - + 'amount', + 'paid_at', ]; const opts = { fields }; try { diff --git a/backend/src/routes/products.js b/backend/src/routes/products.js index 9ca71cc..0cbc4a2 100644 --- a/backend/src/routes/products.js +++ b/backend/src/routes/products.js @@ -26,8 +26,29 @@ router.use(checkCrudPermissions('products')); * type: object * properties: + * name: + * type: string + * default: name + * sku: + * type: string + * default: sku + * description: + * type: string + * default: description + * currency: + * type: string + * default: currency + * stock: + * type: integer + * format: int64 + * price: + * type: integer + * format: int64 + * cost: + * type: integer + * format: int64 */ @@ -285,9 +306,9 @@ router.get('/', wrapAsync(async (req, res) => { req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', - - + const fields = ['id','name','sku','description','currency', + 'stock', + 'price','cost', ]; const opts = { fields }; diff --git a/backend/src/routes/returns.js b/backend/src/routes/returns.js new file mode 100644 index 0000000..f95e5b4 --- /dev/null +++ b/backend/src/routes/returns.js @@ -0,0 +1,436 @@ + +const express = require('express'); + +const ReturnsService = require('../services/returns'); +const ReturnsDBApi = require('../db/api/returns'); +const wrapAsync = require('../helpers').wrapAsync; + + +const router = express.Router(); + +const { parse } = require('json2csv'); + + +const { + checkCrudPermissions, +} = require('../middlewares/check-permissions'); + +router.use(checkCrudPermissions('returns')); + + +/** + * @swagger + * components: + * schemas: + * Returns: + * type: object + * properties: + + * rma_number: + * type: string + * default: rma_number + * reason: + * type: string + * default: reason + + + * refund_amount: + * type: integer + * format: int64 + + * + */ + +/** + * @swagger + * tags: + * name: Returns + * description: The Returns managing API + */ + +/** +* @swagger +* /api/returns: +* post: +* security: +* - bearerAuth: [] +* tags: [Returns] +* summary: Add new item +* description: Add new item +* requestBody: +* required: true +* content: +* application/json: +* schema: +* properties: +* data: +* description: Data of the updated item +* type: object +* $ref: "#/components/schemas/Returns" +* responses: +* 200: +* description: The item was successfully added +* content: +* application/json: +* schema: +* $ref: "#/components/schemas/Returns" +* 401: +* $ref: "#/components/responses/UnauthorizedError" +* 405: +* description: Invalid input data +* 500: +* description: Some server error +*/ +router.post('/', wrapAsync(async (req, res) => { + const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ReturnsService.create(req.body.data, req.currentUser, true, link.host); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/budgets/bulk-import: + * post: + * security: + * - bearerAuth: [] + * tags: [Returns] + * summary: Bulk import items + * description: Bulk import items + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated items + * type: array + * items: + * $ref: "#/components/schemas/Returns" + * responses: + * 200: + * description: The items were successfully imported + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Returns" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + * + */ +router.post('/bulk-import', wrapAsync(async (req, res) => { + const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ReturnsService.bulkImport(req, res, true, link.host); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/returns/{id}: + * put: + * security: + * - bearerAuth: [] + * tags: [Returns] + * summary: Update the data of the selected item + * description: Update the data of the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to update + * required: true + * schema: + * type: string + * requestBody: + * description: Set new item data + * required: true + * content: + * application/json: + * schema: + * properties: + * id: + * description: ID of the updated item + * type: string + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Returns" + * required: + * - id + * responses: + * 200: + * description: The item data was successfully updated + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Returns" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.put('/:id', wrapAsync(async (req, res) => { + await ReturnsService.update(req.body.data, req.body.id, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/returns/{id}: + * delete: + * security: + * - bearerAuth: [] + * tags: [Returns] + * summary: Delete the selected item + * description: Delete the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to delete + * required: true + * schema: + * type: string + * responses: + * 200: + * description: The item was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Returns" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.delete('/:id', wrapAsync(async (req, res) => { + await ReturnsService.remove(req.params.id, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/returns/deleteByIds: + * post: + * security: + * - bearerAuth: [] + * tags: [Returns] + * summary: Delete the selected item list + * description: Delete the selected item list + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * ids: + * description: IDs of the updated items + * type: array + * responses: + * 200: + * description: The items was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Returns" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Items not found + * 500: + * description: Some server error + */ +router.post('/deleteByIds', wrapAsync(async (req, res) => { + await ReturnsService.deleteByIds(req.body.data, req.currentUser); + const payload = true; + res.status(200).send(payload); + })); + +/** + * @swagger + * /api/returns: + * get: + * security: + * - bearerAuth: [] + * tags: [Returns] + * summary: Get all returns + * description: Get all returns + * responses: + * 200: + * description: Returns list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Returns" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error +*/ +router.get('/', wrapAsync(async (req, res) => { + const filetype = req.query.filetype + + const currentUser = req.currentUser; + const payload = await ReturnsDBApi.findAll( + req.query, { currentUser } + ); + if (filetype && filetype === 'csv') { + const fields = ['id','rma_number','reason', + + 'refund_amount', + 'requested_at','processed_at', + ]; + const opts = { fields }; + try { + const csv = parse(payload.rows, opts); + res.status(200).attachment(csv); + res.send(csv) + + } catch (err) { + console.error(err); + } + } else { + res.status(200).send(payload); + } + +})); + +/** + * @swagger + * /api/returns/count: + * get: + * security: + * - bearerAuth: [] + * tags: [Returns] + * summary: Count all returns + * description: Count all returns + * responses: + * 200: + * description: Returns count successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Returns" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/count', wrapAsync(async (req, res) => { + + const currentUser = req.currentUser; + const payload = await ReturnsDBApi.findAll( + req.query, + null, + { countOnly: true, currentUser } + ); + + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/returns/autocomplete: + * get: + * security: + * - bearerAuth: [] + * tags: [Returns] + * summary: Find all returns that match search criteria + * description: Find all returns that match search criteria + * responses: + * 200: + * description: Returns list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Returns" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/autocomplete', async (req, res) => { + + const payload = await ReturnsDBApi.findAllAutocomplete( + req.query.query, + req.query.limit, + req.query.offset, + + ); + + res.status(200).send(payload); +}); + +/** + * @swagger + * /api/returns/{id}: + * get: + * security: + * - bearerAuth: [] + * tags: [Returns] + * summary: Get selected item + * description: Get selected item + * parameters: + * - in: path + * name: id + * description: ID of item to get + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Selected item successfully received + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Returns" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.get('/:id', wrapAsync(async (req, res) => { + const payload = await ReturnsDBApi.findBy( + { id: req.params.id }, + ); + + + + res.status(200).send(payload); +})); + +router.use('/', require('../helpers').commonErrorHandler); + +module.exports = router; diff --git a/backend/src/routes/reviews.js b/backend/src/routes/reviews.js new file mode 100644 index 0000000..4a282d2 --- /dev/null +++ b/backend/src/routes/reviews.js @@ -0,0 +1,435 @@ + +const express = require('express'); + +const ReviewsService = require('../services/reviews'); +const ReviewsDBApi = require('../db/api/reviews'); +const wrapAsync = require('../helpers').wrapAsync; + + +const router = express.Router(); + +const { parse } = require('json2csv'); + + +const { + checkCrudPermissions, +} = require('../middlewares/check-permissions'); + +router.use(checkCrudPermissions('reviews')); + + +/** + * @swagger + * components: + * schemas: + * Reviews: + * type: object + * properties: + + * author_name: + * type: string + * default: author_name + * comment: + * type: string + * default: comment + + * rating: + * type: integer + * format: int64 + + + */ + +/** + * @swagger + * tags: + * name: Reviews + * description: The Reviews managing API + */ + +/** +* @swagger +* /api/reviews: +* post: +* security: +* - bearerAuth: [] +* tags: [Reviews] +* summary: Add new item +* description: Add new item +* requestBody: +* required: true +* content: +* application/json: +* schema: +* properties: +* data: +* description: Data of the updated item +* type: object +* $ref: "#/components/schemas/Reviews" +* responses: +* 200: +* description: The item was successfully added +* content: +* application/json: +* schema: +* $ref: "#/components/schemas/Reviews" +* 401: +* $ref: "#/components/responses/UnauthorizedError" +* 405: +* description: Invalid input data +* 500: +* description: Some server error +*/ +router.post('/', wrapAsync(async (req, res) => { + const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ReviewsService.create(req.body.data, req.currentUser, true, link.host); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/budgets/bulk-import: + * post: + * security: + * - bearerAuth: [] + * tags: [Reviews] + * summary: Bulk import items + * description: Bulk import items + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated items + * type: array + * items: + * $ref: "#/components/schemas/Reviews" + * responses: + * 200: + * description: The items were successfully imported + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Reviews" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + * + */ +router.post('/bulk-import', wrapAsync(async (req, res) => { + const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await ReviewsService.bulkImport(req, res, true, link.host); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/reviews/{id}: + * put: + * security: + * - bearerAuth: [] + * tags: [Reviews] + * summary: Update the data of the selected item + * description: Update the data of the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to update + * required: true + * schema: + * type: string + * requestBody: + * description: Set new item data + * required: true + * content: + * application/json: + * schema: + * properties: + * id: + * description: ID of the updated item + * type: string + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Reviews" + * required: + * - id + * responses: + * 200: + * description: The item data was successfully updated + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Reviews" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.put('/:id', wrapAsync(async (req, res) => { + await ReviewsService.update(req.body.data, req.body.id, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/reviews/{id}: + * delete: + * security: + * - bearerAuth: [] + * tags: [Reviews] + * summary: Delete the selected item + * description: Delete the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to delete + * required: true + * schema: + * type: string + * responses: + * 200: + * description: The item was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Reviews" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.delete('/:id', wrapAsync(async (req, res) => { + await ReviewsService.remove(req.params.id, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/reviews/deleteByIds: + * post: + * security: + * - bearerAuth: [] + * tags: [Reviews] + * summary: Delete the selected item list + * description: Delete the selected item list + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * ids: + * description: IDs of the updated items + * type: array + * responses: + * 200: + * description: The items was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Reviews" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Items not found + * 500: + * description: Some server error + */ +router.post('/deleteByIds', wrapAsync(async (req, res) => { + await ReviewsService.deleteByIds(req.body.data, req.currentUser); + const payload = true; + res.status(200).send(payload); + })); + +/** + * @swagger + * /api/reviews: + * get: + * security: + * - bearerAuth: [] + * tags: [Reviews] + * summary: Get all reviews + * description: Get all reviews + * responses: + * 200: + * description: Reviews list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Reviews" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error +*/ +router.get('/', wrapAsync(async (req, res) => { + const filetype = req.query.filetype + + const currentUser = req.currentUser; + const payload = await ReviewsDBApi.findAll( + req.query, { currentUser } + ); + if (filetype && filetype === 'csv') { + const fields = ['id','author_name','comment', + 'rating', + + 'created', + ]; + const opts = { fields }; + try { + const csv = parse(payload.rows, opts); + res.status(200).attachment(csv); + res.send(csv) + + } catch (err) { + console.error(err); + } + } else { + res.status(200).send(payload); + } + +})); + +/** + * @swagger + * /api/reviews/count: + * get: + * security: + * - bearerAuth: [] + * tags: [Reviews] + * summary: Count all reviews + * description: Count all reviews + * responses: + * 200: + * description: Reviews count successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Reviews" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/count', wrapAsync(async (req, res) => { + + const currentUser = req.currentUser; + const payload = await ReviewsDBApi.findAll( + req.query, + null, + { countOnly: true, currentUser } + ); + + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/reviews/autocomplete: + * get: + * security: + * - bearerAuth: [] + * tags: [Reviews] + * summary: Find all reviews that match search criteria + * description: Find all reviews that match search criteria + * responses: + * 200: + * description: Reviews list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Reviews" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/autocomplete', async (req, res) => { + + const payload = await ReviewsDBApi.findAllAutocomplete( + req.query.query, + req.query.limit, + req.query.offset, + + ); + + res.status(200).send(payload); +}); + +/** + * @swagger + * /api/reviews/{id}: + * get: + * security: + * - bearerAuth: [] + * tags: [Reviews] + * summary: Get selected item + * description: Get selected item + * parameters: + * - in: path + * name: id + * description: ID of item to get + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Selected item successfully received + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Reviews" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.get('/:id', wrapAsync(async (req, res) => { + const payload = await ReviewsDBApi.findBy( + { id: req.params.id }, + ); + + + + res.status(200).send(payload); +})); + +router.use('/', require('../helpers').commonErrorHandler); + +module.exports = router; diff --git a/backend/src/routes/shipments.js b/backend/src/routes/shipments.js index ef2aee3..4b9cfc3 100644 --- a/backend/src/routes/shipments.js +++ b/backend/src/routes/shipments.js @@ -26,9 +26,19 @@ router.use(checkCrudPermissions('shipments')); * type: object * properties: + * tracking_number: + * type: string + * default: tracking_number + * carrier: + * type: string + * default: carrier + * notes: + * type: string + * default: notes + * */ /** @@ -285,10 +295,10 @@ router.get('/', wrapAsync(async (req, res) => { req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', + const fields = ['id','tracking_number','carrier','notes', - + 'shipped_at','delivered_at', ]; const opts = { fields }; try { diff --git a/backend/src/routes/suppliers.js b/backend/src/routes/suppliers.js index 6cb4d11..a8831c2 100644 --- a/backend/src/routes/suppliers.js +++ b/backend/src/routes/suppliers.js @@ -26,6 +26,24 @@ router.use(checkCrudPermissions('suppliers')); * type: object * properties: + * name: + * type: string + * default: name + * contact_name: + * type: string + * default: contact_name + * email: + * type: string + * default: email + * phone: + * type: string + * default: phone + * address: + * type: string + * default: address + * notes: + * type: string + * default: notes @@ -285,7 +303,7 @@ router.get('/', wrapAsync(async (req, res) => { req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', + const fields = ['id','name','contact_name','email','phone','address','notes', diff --git a/backend/src/routes/inventory.js b/backend/src/routes/tags.js similarity index 78% rename from backend/src/routes/inventory.js rename to backend/src/routes/tags.js index 1b35092..8290a78 100644 --- a/backend/src/routes/inventory.js +++ b/backend/src/routes/tags.js @@ -1,8 +1,8 @@ const express = require('express'); -const InventoryService = require('../services/inventory'); -const InventoryDBApi = require('../db/api/inventory'); +const TagsService = require('../services/tags'); +const TagsDBApi = require('../db/api/tags'); const wrapAsync = require('../helpers').wrapAsync; @@ -15,17 +15,23 @@ const { checkCrudPermissions, } = require('../middlewares/check-permissions'); -router.use(checkCrudPermissions('inventory')); +router.use(checkCrudPermissions('tags')); /** * @swagger * components: * schemas: - * Inventory: + * Tags: * type: object * properties: + * name: + * type: string + * default: name + * color: + * type: string + * default: color @@ -34,17 +40,17 @@ router.use(checkCrudPermissions('inventory')); /** * @swagger * tags: - * name: Inventory - * description: The Inventory managing API + * name: Tags + * description: The Tags managing API */ /** * @swagger -* /api/inventory: +* /api/tags: * post: * security: * - bearerAuth: [] -* tags: [Inventory] +* tags: [Tags] * summary: Add new item * description: Add new item * requestBody: @@ -56,14 +62,14 @@ router.use(checkCrudPermissions('inventory')); * data: * description: Data of the updated item * type: object -* $ref: "#/components/schemas/Inventory" +* $ref: "#/components/schemas/Tags" * responses: * 200: * description: The item was successfully added * content: * application/json: * schema: -* $ref: "#/components/schemas/Inventory" +* $ref: "#/components/schemas/Tags" * 401: * $ref: "#/components/responses/UnauthorizedError" * 405: @@ -74,7 +80,7 @@ router.use(checkCrudPermissions('inventory')); router.post('/', wrapAsync(async (req, res) => { const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; const link = new URL(referer); - await InventoryService.create(req.body.data, req.currentUser, true, link.host); + await TagsService.create(req.body.data, req.currentUser, true, link.host); const payload = true; res.status(200).send(payload); })); @@ -85,7 +91,7 @@ router.post('/', wrapAsync(async (req, res) => { * post: * security: * - bearerAuth: [] - * tags: [Inventory] + * tags: [Tags] * summary: Bulk import items * description: Bulk import items * requestBody: @@ -98,14 +104,14 @@ router.post('/', wrapAsync(async (req, res) => { * description: Data of the updated items * type: array * items: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * responses: * 200: * description: The items were successfully imported * content: * application/json: * schema: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * 401: * $ref: "#/components/responses/UnauthorizedError" * 405: @@ -117,18 +123,18 @@ router.post('/', wrapAsync(async (req, res) => { router.post('/bulk-import', wrapAsync(async (req, res) => { const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; const link = new URL(referer); - await InventoryService.bulkImport(req, res, true, link.host); + await TagsService.bulkImport(req, res, true, link.host); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/inventory/{id}: + * /api/tags/{id}: * put: * security: * - bearerAuth: [] - * tags: [Inventory] + * tags: [Tags] * summary: Update the data of the selected item * description: Update the data of the selected item * parameters: @@ -151,7 +157,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * data: * description: Data of the updated item * type: object - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * required: * - id * responses: @@ -160,7 +166,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * 400: * description: Invalid ID supplied * 401: @@ -171,18 +177,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * description: Some server error */ router.put('/:id', wrapAsync(async (req, res) => { - await InventoryService.update(req.body.data, req.body.id, req.currentUser); + await TagsService.update(req.body.data, req.body.id, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/inventory/{id}: + * /api/tags/{id}: * delete: * security: * - bearerAuth: [] - * tags: [Inventory] + * tags: [Tags] * summary: Delete the selected item * description: Delete the selected item * parameters: @@ -198,7 +204,7 @@ router.put('/:id', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * 400: * description: Invalid ID supplied * 401: @@ -209,18 +215,18 @@ router.put('/:id', wrapAsync(async (req, res) => { * description: Some server error */ router.delete('/:id', wrapAsync(async (req, res) => { - await InventoryService.remove(req.params.id, req.currentUser); + await TagsService.remove(req.params.id, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/inventory/deleteByIds: + * /api/tags/deleteByIds: * post: * security: * - bearerAuth: [] - * tags: [Inventory] + * tags: [Tags] * summary: Delete the selected item list * description: Delete the selected item list * requestBody: @@ -238,7 +244,7 @@ router.delete('/:id', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -247,29 +253,29 @@ router.delete('/:id', wrapAsync(async (req, res) => { * description: Some server error */ router.post('/deleteByIds', wrapAsync(async (req, res) => { - await InventoryService.deleteByIds(req.body.data, req.currentUser); + await TagsService.deleteByIds(req.body.data, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/inventory: + * /api/tags: * get: * security: * - bearerAuth: [] - * tags: [Inventory] - * summary: Get all inventory - * description: Get all inventory + * tags: [Tags] + * summary: Get all tags + * description: Get all tags * responses: * 200: - * description: Inventory list successfully received + * description: Tags list successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -281,11 +287,11 @@ router.get('/', wrapAsync(async (req, res) => { const filetype = req.query.filetype const currentUser = req.currentUser; - const payload = await InventoryDBApi.findAll( + const payload = await TagsDBApi.findAll( req.query, { currentUser } ); if (filetype && filetype === 'csv') { - const fields = ['id', + const fields = ['id','name','color', @@ -307,22 +313,22 @@ router.get('/', wrapAsync(async (req, res) => { /** * @swagger - * /api/inventory/count: + * /api/tags/count: * get: * security: * - bearerAuth: [] - * tags: [Inventory] - * summary: Count all inventory - * description: Count all inventory + * tags: [Tags] + * summary: Count all tags + * description: Count all tags * responses: * 200: - * description: Inventory count successfully received + * description: Tags count successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -333,7 +339,7 @@ router.get('/', wrapAsync(async (req, res) => { router.get('/count', wrapAsync(async (req, res) => { const currentUser = req.currentUser; - const payload = await InventoryDBApi.findAll( + const payload = await TagsDBApi.findAll( req.query, null, { countOnly: true, currentUser } @@ -344,22 +350,22 @@ router.get('/count', wrapAsync(async (req, res) => { /** * @swagger - * /api/inventory/autocomplete: + * /api/tags/autocomplete: * get: * security: * - bearerAuth: [] - * tags: [Inventory] - * summary: Find all inventory that match search criteria - * description: Find all inventory that match search criteria + * tags: [Tags] + * summary: Find all tags that match search criteria + * description: Find all tags that match search criteria * responses: * 200: - * description: Inventory list successfully received + * description: Tags list successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -369,7 +375,7 @@ router.get('/count', wrapAsync(async (req, res) => { */ router.get('/autocomplete', async (req, res) => { - const payload = await InventoryDBApi.findAllAutocomplete( + const payload = await TagsDBApi.findAllAutocomplete( req.query.query, req.query.limit, req.query.offset, @@ -381,11 +387,11 @@ router.get('/autocomplete', async (req, res) => { /** * @swagger - * /api/inventory/{id}: + * /api/tags/{id}: * get: * security: * - bearerAuth: [] - * tags: [Inventory] + * tags: [Tags] * summary: Get selected item * description: Get selected item * parameters: @@ -401,7 +407,7 @@ router.get('/autocomplete', async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Inventory" + * $ref: "#/components/schemas/Tags" * 400: * description: Invalid ID supplied * 401: @@ -412,7 +418,7 @@ router.get('/autocomplete', async (req, res) => { * description: Some server error */ router.get('/:id', wrapAsync(async (req, res) => { - const payload = await InventoryDBApi.findBy( + const payload = await TagsDBApi.findBy( { id: req.params.id }, ); diff --git a/backend/src/services/inventory.js b/backend/src/services/discounts.js similarity index 85% rename from backend/src/services/inventory.js rename to backend/src/services/discounts.js index efd69fe..42cde1a 100644 --- a/backend/src/services/inventory.js +++ b/backend/src/services/discounts.js @@ -1,5 +1,5 @@ const db = require('../db/models'); -const InventoryDBApi = require('../db/api/inventory'); +const DiscountsDBApi = require('../db/api/discounts'); const processFile = require("../middlewares/upload"); const ValidationError = require('./notifications/errors/validation'); const csv = require('csv-parser'); @@ -11,11 +11,11 @@ const stream = require('stream'); -module.exports = class InventoryService { +module.exports = class DiscountsService { static async create(data, currentUser) { const transaction = await db.sequelize.transaction(); try { - await InventoryDBApi.create( + await DiscountsDBApi.create( data, { currentUser, @@ -51,7 +51,7 @@ module.exports = class InventoryService { .on('error', (error) => reject(error)); }) - await InventoryDBApi.bulkImport(results, { + await DiscountsDBApi.bulkImport(results, { transaction, ignoreDuplicates: true, validate: true, @@ -68,18 +68,18 @@ module.exports = class InventoryService { static async update(data, id, currentUser) { const transaction = await db.sequelize.transaction(); try { - let inventory = await InventoryDBApi.findBy( + let discounts = await DiscountsDBApi.findBy( {id}, {transaction}, ); - if (!inventory) { + if (!discounts) { throw new ValidationError( - 'inventoryNotFound', + 'discountsNotFound', ); } - const updatedInventory = await InventoryDBApi.update( + const updatedDiscounts = await DiscountsDBApi.update( id, data, { @@ -89,7 +89,7 @@ module.exports = class InventoryService { ); await transaction.commit(); - return updatedInventory; + return updatedDiscounts; } catch (error) { await transaction.rollback(); @@ -101,7 +101,7 @@ module.exports = class InventoryService { const transaction = await db.sequelize.transaction(); try { - await InventoryDBApi.deleteByIds(ids, { + await DiscountsDBApi.deleteByIds(ids, { currentUser, transaction, }); @@ -117,7 +117,7 @@ module.exports = class InventoryService { const transaction = await db.sequelize.transaction(); try { - await InventoryDBApi.remove( + await DiscountsDBApi.remove( id, { currentUser, diff --git a/backend/src/services/inventory_movements.js b/backend/src/services/inventory_movements.js new file mode 100644 index 0000000..3038d90 --- /dev/null +++ b/backend/src/services/inventory_movements.js @@ -0,0 +1,138 @@ +const db = require('../db/models'); +const Inventory_movementsDBApi = require('../db/api/inventory_movements'); +const processFile = require("../middlewares/upload"); +const ValidationError = require('./notifications/errors/validation'); +const csv = require('csv-parser'); +const axios = require('axios'); +const config = require('../config'); +const stream = require('stream'); + + + + + +module.exports = class Inventory_movementsService { + static async create(data, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + await Inventory_movementsDBApi.create( + data, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + }; + + static async bulkImport(req, res, sendInvitationEmails = true, host) { + const transaction = await db.sequelize.transaction(); + + try { + await processFile(req, res); + const bufferStream = new stream.PassThrough(); + const results = []; + + await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream + + await new Promise((resolve, reject) => { + bufferStream + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', async () => { + console.log('CSV results', results); + resolve(); + }) + .on('error', (error) => reject(error)); + }) + + await Inventory_movementsDBApi.bulkImport(results, { + transaction, + ignoreDuplicates: true, + validate: true, + currentUser: req.currentUser + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async update(data, id, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + let inventory_movements = await Inventory_movementsDBApi.findBy( + {id}, + {transaction}, + ); + + if (!inventory_movements) { + throw new ValidationError( + 'inventory_movementsNotFound', + ); + } + + const updatedInventory_movements = await Inventory_movementsDBApi.update( + id, + data, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + return updatedInventory_movements; + + } catch (error) { + await transaction.rollback(); + throw error; + } + }; + + static async deleteByIds(ids, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await Inventory_movementsDBApi.deleteByIds(ids, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async remove(id, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await Inventory_movementsDBApi.remove( + id, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + +}; + + diff --git a/backend/src/services/notifications/list.js b/backend/src/services/notifications/list.js index c15e443..28b32ad 100644 --- a/backend/src/services/notifications/list.js +++ b/backend/src/services/notifications/list.js @@ -1,6 +1,6 @@ const errors = { app: { - title: 'StoreOps Manager', + title: 'Store Operations Manager', }, auth: { diff --git a/backend/src/services/product_variants.js b/backend/src/services/returns.js similarity index 82% rename from backend/src/services/product_variants.js rename to backend/src/services/returns.js index 5e14081..da6d2c3 100644 --- a/backend/src/services/product_variants.js +++ b/backend/src/services/returns.js @@ -1,5 +1,5 @@ const db = require('../db/models'); -const Product_variantsDBApi = require('../db/api/product_variants'); +const ReturnsDBApi = require('../db/api/returns'); const processFile = require("../middlewares/upload"); const ValidationError = require('./notifications/errors/validation'); const csv = require('csv-parser'); @@ -11,11 +11,11 @@ const stream = require('stream'); -module.exports = class Product_variantsService { +module.exports = class ReturnsService { static async create(data, currentUser) { const transaction = await db.sequelize.transaction(); try { - await Product_variantsDBApi.create( + await ReturnsDBApi.create( data, { currentUser, @@ -51,7 +51,7 @@ module.exports = class Product_variantsService { .on('error', (error) => reject(error)); }) - await Product_variantsDBApi.bulkImport(results, { + await ReturnsDBApi.bulkImport(results, { transaction, ignoreDuplicates: true, validate: true, @@ -68,18 +68,18 @@ module.exports = class Product_variantsService { static async update(data, id, currentUser) { const transaction = await db.sequelize.transaction(); try { - let product_variants = await Product_variantsDBApi.findBy( + let returns = await ReturnsDBApi.findBy( {id}, {transaction}, ); - if (!product_variants) { + if (!returns) { throw new ValidationError( - 'product_variantsNotFound', + 'returnsNotFound', ); } - const updatedProduct_variants = await Product_variantsDBApi.update( + const updatedReturns = await ReturnsDBApi.update( id, data, { @@ -89,7 +89,7 @@ module.exports = class Product_variantsService { ); await transaction.commit(); - return updatedProduct_variants; + return updatedReturns; } catch (error) { await transaction.rollback(); @@ -101,7 +101,7 @@ module.exports = class Product_variantsService { const transaction = await db.sequelize.transaction(); try { - await Product_variantsDBApi.deleteByIds(ids, { + await ReturnsDBApi.deleteByIds(ids, { currentUser, transaction, }); @@ -117,7 +117,7 @@ module.exports = class Product_variantsService { const transaction = await db.sequelize.transaction(); try { - await Product_variantsDBApi.remove( + await ReturnsDBApi.remove( id, { currentUser, diff --git a/backend/src/services/reviews.js b/backend/src/services/reviews.js new file mode 100644 index 0000000..86251f6 --- /dev/null +++ b/backend/src/services/reviews.js @@ -0,0 +1,138 @@ +const db = require('../db/models'); +const ReviewsDBApi = require('../db/api/reviews'); +const processFile = require("../middlewares/upload"); +const ValidationError = require('./notifications/errors/validation'); +const csv = require('csv-parser'); +const axios = require('axios'); +const config = require('../config'); +const stream = require('stream'); + + + + + +module.exports = class ReviewsService { + static async create(data, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + await ReviewsDBApi.create( + data, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + }; + + static async bulkImport(req, res, sendInvitationEmails = true, host) { + const transaction = await db.sequelize.transaction(); + + try { + await processFile(req, res); + const bufferStream = new stream.PassThrough(); + const results = []; + + await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream + + await new Promise((resolve, reject) => { + bufferStream + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', async () => { + console.log('CSV results', results); + resolve(); + }) + .on('error', (error) => reject(error)); + }) + + await ReviewsDBApi.bulkImport(results, { + transaction, + ignoreDuplicates: true, + validate: true, + currentUser: req.currentUser + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async update(data, id, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + let reviews = await ReviewsDBApi.findBy( + {id}, + {transaction}, + ); + + if (!reviews) { + throw new ValidationError( + 'reviewsNotFound', + ); + } + + const updatedReviews = await ReviewsDBApi.update( + id, + data, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + return updatedReviews; + + } catch (error) { + await transaction.rollback(); + throw error; + } + }; + + static async deleteByIds(ids, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await ReviewsDBApi.deleteByIds(ids, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async remove(id, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await ReviewsDBApi.remove( + id, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + +}; + + diff --git a/backend/src/services/search.js b/backend/src/services/search.js index be30612..c82891d 100644 --- a/backend/src/services/search.js +++ b/backend/src/services/search.js @@ -66,51 +66,192 @@ module.exports = class SearchService { - - - - + "customers": [ + + "name", + + "email", + + "phone", + + "company", + + "address", + + "notes", + + ], - - - - + "categories": [ + + "name", + + "description", + + ], - - - - + "suppliers": [ + + "name", + + "contact_name", + + "email", + + "phone", + + "address", + + "notes", + + ], - - - - + "tags": [ + + "name", + + "color", + + ], + "products": [ + + "name", + + "sku", + + "description", + + "currency", + + ], + + "inventory_movements": [ + + "note", + + ], + + + + + + + "reviews": [ + + "author_name", + + "comment", + + ], + + + + + + + "discounts": [ + + "code", + + "description", + + ], + + + + + + + "orders": [ + + "order_number", + + "shipping_address", + + "billing_address", + + ], + + + + + + + "order_items": [ + + "product_name", + + "sku", + + ], + + + + + + + "payments": [ + + "transaction_ref", + + "provider", + + ], + + + + + + + "shipments": [ + + "tracking_number", + + "carrier", + + "notes", + + ], + + + + + + + "returns": [ + + "rma_number", + + "reason", + + ], + }; const columnsInt = { @@ -124,9 +265,11 @@ module.exports = class SearchService { - - - + "customers": [ + + "loyalty_points", + + ], @@ -144,6 +287,86 @@ module.exports = class SearchService { + "products": [ + + "price", + + "cost", + + "stock", + + ], + + + + + + "inventory_movements": [ + + "change", + + ], + + + + + + "reviews": [ + + "rating", + + ], + + + + + + "discounts": [ + + "value", + + ], + + + + + + "orders": [ + + "subtotal", + + "tax", + + "shipping_fee", + + "total", + + ], + + + + + + "order_items": [ + + "quantity", + + "unit_price", + + "total", + + ], + + + + + + "payments": [ + + "amount", + + ], + @@ -152,13 +375,11 @@ module.exports = class SearchService { - - - - - - - + "returns": [ + + "refund_amount", + + ], }; diff --git a/backend/src/services/tags.js b/backend/src/services/tags.js new file mode 100644 index 0000000..71483e2 --- /dev/null +++ b/backend/src/services/tags.js @@ -0,0 +1,138 @@ +const db = require('../db/models'); +const TagsDBApi = require('../db/api/tags'); +const processFile = require("../middlewares/upload"); +const ValidationError = require('./notifications/errors/validation'); +const csv = require('csv-parser'); +const axios = require('axios'); +const config = require('../config'); +const stream = require('stream'); + + + + + +module.exports = class TagsService { + static async create(data, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + await TagsDBApi.create( + data, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + }; + + static async bulkImport(req, res, sendInvitationEmails = true, host) { + const transaction = await db.sequelize.transaction(); + + try { + await processFile(req, res); + const bufferStream = new stream.PassThrough(); + const results = []; + + await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream + + await new Promise((resolve, reject) => { + bufferStream + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', async () => { + console.log('CSV results', results); + resolve(); + }) + .on('error', (error) => reject(error)); + }) + + await TagsDBApi.bulkImport(results, { + transaction, + ignoreDuplicates: true, + validate: true, + currentUser: req.currentUser + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async update(data, id, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + let tags = await TagsDBApi.findBy( + {id}, + {transaction}, + ); + + if (!tags) { + throw new ValidationError( + 'tagsNotFound', + ); + } + + const updatedTags = await TagsDBApi.update( + id, + data, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + return updatedTags; + + } catch (error) { + await transaction.rollback(); + throw error; + } + }; + + static async deleteByIds(ids, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await TagsDBApi.deleteByIds(ids, { + currentUser, + transaction, + }); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + static async remove(id, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + await TagsDBApi.remove( + id, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } + + +}; + + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 4170374..c803e34 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -25,7 +25,7 @@ services: - ./data/db:/var/lib/postgresql/data environment: - POSTGRES_HOST_AUTH_METHOD=trust - - POSTGRES_DB=db_storeops_manager + - POSTGRES_DB=db_store_operations_manager ports: - "5432:5432" logging: diff --git a/frontend/README.md b/frontend/README.md index b25e7e4..5235cd6 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,4 +1,4 @@ -# StoreOps Manager +# Store Operations Manager ## This project was generated by Flatlogic Platform. ## Install diff --git a/frontend/src/colors.ts b/frontend/src/colors.ts index 92a1dbb..71e116a 100644 --- a/frontend/src/colors.ts +++ b/frontend/src/colors.ts @@ -1,7 +1,7 @@ import type { ColorButtonKey } from './interfaces' export const gradientBgBase = 'bg-gradient-to-tr' -export const colorBgBase = "bg-skyBlueTheme-mainBG" +export const colorBgBase = "bg-violet-50/50" export const gradientBgPurplePink = `${gradientBgBase} from-purple-400 via-pink-500 to-red-500` export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}` export const gradientBgDark = `${gradientBgBase} from-dark-700 via-dark-900 to-dark-800`; @@ -9,7 +9,7 @@ export const gradientBgPinkRed = `${gradientBgBase} from-pink-400 via-red-500 to export const colorsBgLight = { white: 'bg-white text-black', - light: ' bg-skyBlueTheme-outsideCardColor text-primaryText text-primaryText dark:bg-dark-900 dark:text-white', + light: ' bg-white text-black text-black dark:bg-dark-900 dark:text-white', contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black', success: 'bg-emerald-500 border-emerald-500 dark:bg-pavitra-blue dark:border-pavitra-blue text-white', danger: 'bg-red-500 border-red-500 text-white', @@ -19,7 +19,7 @@ export const colorsBgLight = { export const colorsText = { white: 'text-black dark:text-slate-100', - light: 'text-primaryText dark:text-slate-400', + light: 'text-gray-700 dark:text-slate-400', contrast: 'dark:text-white', success: 'text-emerald-500', danger: 'text-red-500', @@ -50,13 +50,13 @@ export const getButtonColor = ( const colors = { ring: { white: 'ring-gray-200 dark:ring-gray-500', - whiteDark: 'ring-skyBlueTheme-outsideCardColor dark:ring-dark-500', + whiteDark: 'ring-gray-200 dark:ring-dark-500', lightDark: 'ring-gray-200 dark:ring-gray-500', contrast: 'ring-gray-300 dark:ring-gray-400', success: 'ring-emerald-300 dark:ring-pavitra-blue', danger: 'ring-red-300 dark:ring-red-700', warning: 'ring-yellow-300 dark:ring-yellow-700', - info: "ring-skyBlueTheme-buttonColor dark:ring-pavitra-blue", + info: "ring-blue-300 dark:ring-pavitra-blue", }, active: { white: 'bg-gray-100', @@ -66,21 +66,21 @@ export const getButtonColor = ( success: 'bg-emerald-700 dark:bg-pavitra-blue', danger: 'bg-red-700 dark:bg-red-600', warning: 'bg-yellow-700 dark:bg-yellow-600', - info: 'bg-skyBlueTheme-buttonColor dark:bg-pavitra-blue', + info: 'bg-blue-700 dark:bg-pavitra-blue', }, bg: { white: 'bg-white text-black', - whiteDark: 'bg-skyBlueTheme-outsideCardColor text-primaryText dark:bg-dark-900 dark:text-white', + whiteDark: 'bg-white text-black dark:bg-dark-900 dark:text-white', lightDark: 'bg-gray-100 text-black dark:bg-slate-800 dark:text-white', contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black', success: 'bg-emerald-600 dark:bg-pavitra-blue text-white', - danger: 'bg-skyBlueTheme-outsideCardColor text-red-500 dark:text-white dark:bg-red-500 ', + danger: 'bg-red-600 text-white dark:bg-red-500 ', warning: 'bg-yellow-600 dark:bg-yellow-500 text-white', - info: " bg-skyBlueTheme-buttonColor dark:bg-pavitra-blue text-white ", + info: " bg-blue-600 dark:bg-pavitra-blue text-white ", }, bgHover: { white: 'hover:bg-gray-100', - whiteDark: 'hover:bg-skyBlueTheme-outsideCardColor hover:dark:bg-dark-800', + whiteDark: 'hover:bg-gray-100 hover:dark:bg-dark-800', lightDark: 'hover:bg-gray-200 hover:dark:bg-slate-700', contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100', success: @@ -89,24 +89,24 @@ export const getButtonColor = ( 'hover:bg-red-700 hover:border-red-700 hover:dark:bg-red-600 hover:dark:border-red-600', warning: 'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600', - info: "hover:bg-skyBlueTheme-800 hover:border-skyBlueTheme-buttonColor hover:dark:bg-pavitra-blue/80 hover:dark:border-pavitra-blue/80", + info: "hover:bg-blue-700 hover:border-blue-700 hover:dark:bg-pavitra-blue/80 hover:dark:border-pavitra-blue/80", }, borders: { white: 'border-white', - whiteDark: 'border-skyBlueTheme-outsideCardColor dark:border-dark-900', + whiteDark: 'border-white dark:border-dark-900', lightDark: 'border-gray-100 dark:border-slate-800', contrast: 'border-gray-800 dark:border-white', success: 'border-emerald-600 dark:border-pavitra-blue', danger: 'border-red-600 dark:border-red-500', warning: 'border-yellow-600 dark:border-yellow-500', - info: "border-skyBlueTheme-buttonColor border-blue-600 dark:border-pavitra-blue", + info: "border-blue-600 border-blue-600 dark:border-pavitra-blue", }, text: { contrast: 'dark:text-slate-100', success: 'text-emerald-600 dark:text-pavitra-blue', danger: 'text-red-600 dark:text-red-500', warning: 'text-yellow-600 dark:text-yellow-500', - info: ' dark:text-pavitra-blue', + info: 'text-blue-600 dark:text-pavitra-blue', }, outlineHover: { contrast: @@ -116,7 +116,7 @@ export const getButtonColor = ( 'hover:bg-red-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-red-600', warning: 'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600', - info: "hover:bg-skyBlueTheme-buttonColor text-skyBlueTheme-buttonColor hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue", + info: "hover:bg-blue-600 hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue", }, } diff --git a/frontend/src/components/AsideMenuLayer.tsx b/frontend/src/components/AsideMenuLayer.tsx index 73579db..74c3657 100644 --- a/frontend/src/components/AsideMenuLayer.tsx +++ b/frontend/src/components/AsideMenuLayer.tsx @@ -39,7 +39,7 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props >
- StoreOps Manager + Store Operations Manager
diff --git a/frontend/src/components/Categories/CardCategories.tsx b/frontend/src/components/Categories/CardCategories.tsx index b8f0b49..914c13e 100644 --- a/frontend/src/components/Categories/CardCategories.tsx +++ b/frontend/src/components/Categories/CardCategories.tsx @@ -76,6 +76,42 @@ const CardCategories = ({
+ +
+
CategoryName
+
+
+ { item.name } +
+
+
+ + + + +
+
Description
+
+
+ { item.description } +
+
+
+ + + + +
+
ParentCategory
+
+
+ { dataFormatter.categoriesOneListFormatter(item.parent) } +
+
+
+ + +
))} diff --git a/frontend/src/components/Categories/ListCategories.tsx b/frontend/src/components/Categories/ListCategories.tsx index aebd856..d45836f 100644 --- a/frontend/src/components/Categories/ListCategories.tsx +++ b/frontend/src/components/Categories/ListCategories.tsx @@ -37,7 +37,7 @@ const ListCategories = ({ categories, loading, onDelete, currentPage, numPages, {!loading && categories.map((item) => (
-
+
+ +
+

CategoryName

+

{ item.name }

+
+ + + + +
+

Description

+

{ item.description }

+
+ + + + +
+

ParentCategory

+

{ dataFormatter.categoriesOneListFormatter(item.parent) }

+
+ + + @@ -443,7 +443,7 @@ const TableSampleCategories = ({ filterItems, setFilterItems, filters, showGrid {categories && Array.isArray(categories) && !showGrid && ( - value?.id, + getOptionLabel: (value: any) => value?.label, + valueOptions: await callOptionsApi('categories'), + valueGetter: (params: GridValueGetterParams) => + params?.value?.id ?? params?.value, + + }, + { field: 'actions', type: 'actions', diff --git a/frontend/src/components/Customers/CardCustomers.tsx b/frontend/src/components/Customers/CardCustomers.tsx index df8c040..dfc18ec 100644 --- a/frontend/src/components/Customers/CardCustomers.tsx +++ b/frontend/src/components/Customers/CardCustomers.tsx @@ -76,6 +76,90 @@ const CardCustomers = ({
+ +
+
CustomerName
+
+
+ { item.name } +
+
+
+ + + + +
+
Email
+
+
+ { item.email } +
+
+
+ + + + +
+
Phone
+
+
+ { item.phone } +
+
+
+ + + + +
+
Company
+
+
+ { item.company } +
+
+
+ + + + +
+
Address
+
+
+ { item.address } +
+
+
+ + + + +
+
Notes
+
+
+ { item.notes } +
+
+
+ + + + +
+
LoyaltyPoints
+
+
+ { item.loyalty_points } +
+
+
+ + +
))} diff --git a/frontend/src/components/Customers/ListCustomers.tsx b/frontend/src/components/Customers/ListCustomers.tsx index a1180fd..f1c72e4 100644 --- a/frontend/src/components/Customers/ListCustomers.tsx +++ b/frontend/src/components/Customers/ListCustomers.tsx @@ -37,7 +37,7 @@ const ListCustomers = ({ customers, loading, onDelete, currentPage, numPages, on {!loading && customers.map((item) => (
-
+
+ +
+

CustomerName

+

{ item.name }

+
+ + + + +
+

Email

+

{ item.email }

+
+ + + + +
+

Phone

+

{ item.phone }

+
+ + + + +
+

Company

+

{ item.company }

+
+ + + + +
+

Address

+

{ item.address }

+
+ + + + +
+

Notes

+

{ item.notes }

+
+ + + + +
+

LoyaltyPoints

+

{ item.loyalty_points }

+
+ + + @@ -442,21 +440,10 @@ const TableSampleCustomers = ({ filterItems, setFilterItems, filters, showGrid } - {customers && Array.isArray(customers) && !showGrid && ( - - )} + {dataGrid} - {showGrid && dataGrid} - {selectedRows.length > 0 && createPortal( diff --git a/frontend/src/components/Customers/configureCustomersCols.tsx b/frontend/src/components/Customers/configureCustomersCols.tsx index 242223f..4938aa8 100644 --- a/frontend/src/components/Customers/configureCustomersCols.tsx +++ b/frontend/src/components/Customers/configureCustomersCols.tsx @@ -41,6 +41,112 @@ export const loadColumns = async ( return [ + { + field: 'name', + headerName: 'CustomerName', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'email', + headerName: 'Email', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'phone', + headerName: 'Phone', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'company', + headerName: 'Company', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'address', + headerName: 'Address', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'notes', + headerName: 'Notes', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'loyalty_points', + headerName: 'LoyaltyPoints', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + type: 'number', + + }, + { field: 'actions', type: 'actions', diff --git a/frontend/src/components/Discounts/CardDiscounts.tsx b/frontend/src/components/Discounts/CardDiscounts.tsx new file mode 100644 index 0000000..3f6f462 --- /dev/null +++ b/frontend/src/components/Discounts/CardDiscounts.tsx @@ -0,0 +1,183 @@ +import React from 'react'; +import ImageField from '../ImageField'; +import ListActionsPopover from '../ListActionsPopover'; +import { useAppSelector } from '../../stores/hooks'; +import dataFormatter from '../../helpers/dataFormatter'; +import { Pagination } from '../Pagination'; +import {saveFile} from "../../helpers/fileSaver"; +import LoadingSpinner from "../LoadingSpinner"; +import Link from 'next/link'; + +import {hasPermission} from "../../helpers/userPermissions"; + + +type Props = { + discounts: any[]; + loading: boolean; + onDelete: (id: string) => void; + currentPage: number; + numPages: number; + onPageChange: (page: number) => void; +}; + +const CardDiscounts = ({ + discounts, + loading, + onDelete, + currentPage, + numPages, + onPageChange, +}: Props) => { + const asideScrollbarsStyle = useAppSelector( + (state) => state.style.asideScrollbarsStyle, + ); + const bgColor = useAppSelector((state) => state.style.cardsColor); + const darkMode = useAppSelector((state) => state.style.darkMode); + const corners = useAppSelector((state) => state.style.corners); + const focusRing = useAppSelector((state) => state.style.focusRingColor); + + const currentUser = useAppSelector((state) => state.auth.currentUser); + const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_DISCOUNTS') + + + return ( +
+ {loading && } +
    + {!loading && discounts.map((item, index) => ( +
  • + +
    + + + {item.code} + + + +
    + +
    +
    +
    + + +
    +
    Code
    +
    +
    + { item.code } +
    +
    +
    + + + + +
    +
    Description
    +
    +
    + { item.description } +
    +
    +
    + + + + +
    +
    Type
    +
    +
    + { item.type } +
    +
    +
    + + + + +
    +
    Value
    +
    +
    + { item.value } +
    +
    +
    + + + + +
    +
    Active
    +
    +
    + { dataFormatter.booleanFormatter(item.active) } +
    +
    +
    + + + + +
    +
    StartsAt
    +
    +
    + { dataFormatter.dateTimeFormatter(item.starts) } +
    +
    +
    + + + + +
    +
    EndsAt
    +
    +
    + { dataFormatter.dateTimeFormatter(item.ends) } +
    +
    +
    + + + +
    +
  • + ))} + {!loading && discounts.length === 0 && ( +
    +

    No data to display

    +
    + )} +
+
+ +
+
+ ); +}; + +export default CardDiscounts; diff --git a/frontend/src/components/Discounts/ListDiscounts.tsx b/frontend/src/components/Discounts/ListDiscounts.tsx new file mode 100644 index 0000000..0000c73 --- /dev/null +++ b/frontend/src/components/Discounts/ListDiscounts.tsx @@ -0,0 +1,136 @@ +import React from 'react'; +import CardBox from '../CardBox'; +import ImageField from '../ImageField'; +import dataFormatter from '../../helpers/dataFormatter'; +import {saveFile} from "../../helpers/fileSaver"; +import ListActionsPopover from "../ListActionsPopover"; +import {useAppSelector} from "../../stores/hooks"; +import {Pagination} from "../Pagination"; +import LoadingSpinner from "../LoadingSpinner"; +import Link from 'next/link'; + +import {hasPermission} from "../../helpers/userPermissions"; + + +type Props = { + discounts: any[]; + loading: boolean; + onDelete: (id: string) => void; + currentPage: number; + numPages: number; + onPageChange: (page: number) => void; +}; + +const ListDiscounts = ({ discounts, loading, onDelete, currentPage, numPages, onPageChange }: Props) => { + + const currentUser = useAppSelector((state) => state.auth.currentUser); + const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_DISCOUNTS') + + const corners = useAppSelector((state) => state.style.corners); + const bgColor = useAppSelector((state) => state.style.cardsColor); + + + return ( + <> +
+ {loading && } + {!loading && discounts.map((item) => ( +
+ +
+ + dark:divide-dark-700 overflow-x-auto' + } + > + + +
+

Code

+

{ item.code }

+
+ + + + +
+

Description

+

{ item.description }

+
+ + + + +
+

Type

+

{ item.type }

+
+ + + + +
+

Value

+

{ item.value }

+
+ + + + +
+

Active

+

{ dataFormatter.booleanFormatter(item.active) }

+
+ + + + +
+

StartsAt

+

{ dataFormatter.dateTimeFormatter(item.starts) }

+
+ + + + +
+

EndsAt

+

{ dataFormatter.dateTimeFormatter(item.ends) }

+
+ + + + + +
+
+
+ ))} + {!loading && discounts.length === 0 && ( +
+

No data to display

+
+ )} +
+
+ +
+ + ) +}; + +export default ListDiscounts \ No newline at end of file diff --git a/frontend/src/components/Product_variants/TableProduct_variants.tsx b/frontend/src/components/Discounts/TableDiscounts.tsx similarity index 95% rename from frontend/src/components/Product_variants/TableProduct_variants.tsx rename to frontend/src/components/Discounts/TableDiscounts.tsx index f3a9f34..72fb8a1 100644 --- a/frontend/src/components/Product_variants/TableProduct_variants.tsx +++ b/frontend/src/components/Discounts/TableDiscounts.tsx @@ -4,7 +4,7 @@ import { ToastContainer, toast } from 'react-toastify'; import BaseButton from '../BaseButton' import CardBoxModal from '../CardBoxModal' import CardBox from "../CardBox"; -import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/product_variants/product_variantsSlice' +import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/discounts/discountsSlice' import { useAppDispatch, useAppSelector } from '../../stores/hooks' import { useRouter } from 'next/router' import { Field, Form, Formik } from "formik"; @@ -12,7 +12,7 @@ import { DataGrid, GridColDef, } from '@mui/x-data-grid'; -import {loadColumns} from "./configureProduct_variantsCols"; +import {loadColumns} from "./configureDiscountsCols"; import _ from 'lodash'; import dataFormatter from '../../helpers/dataFormatter' import {dataGridStyles} from "../../styles"; @@ -21,7 +21,7 @@ import {dataGridStyles} from "../../styles"; const perPage = 10 -const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, showGrid }) => { +const TableSampleDiscounts = ({ filterItems, setFilterItems, filters, showGrid }) => { const notify = (type, msg) => toast( msg, {type, position: "bottom-center"}); const dispatch = useAppDispatch(); @@ -40,7 +40,7 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho }, ]); - const { product_variants, loading, count, notify: product_variantsNotify, refetch } = useAppSelector((state) => state.product_variants) + const { discounts, loading, count, notify: discountsNotify, refetch } = useAppSelector((state) => state.discounts) const { currentUser } = useAppSelector((state) => state.auth); const focusRing = useAppSelector((state) => state.style.focusRingColor); const bgColor = useAppSelector((state) => state.style.bgLayoutColor); @@ -60,10 +60,10 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho }; useEffect(() => { - if (product_variantsNotify.showNotification) { - notify(product_variantsNotify.typeNotification, product_variantsNotify.textNotification); + if (discountsNotify.showNotification) { + notify(discountsNotify.typeNotification, discountsNotify.textNotification); } - }, [product_variantsNotify.showNotification]); + }, [discountsNotify.showNotification]); useEffect(() => { if (!currentUser) return; @@ -177,7 +177,7 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho loadColumns( handleDeleteModalAction, - `product_variants`, + `discounts`, currentUser, ).then((newCols) => setColumns(newCols)); }, [currentUser]); @@ -215,7 +215,7 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho sx={dataGridStyles} className={'datagrid--table'} getRowClassName={() => `datagrid--row`} - rows={product_variants ?? []} + rows={discounts ?? []} columns={columns} initialState={{ pagination: { @@ -412,13 +412,13 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
@@ -460,4 +460,4 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho ) } -export default TableSampleProduct_variants +export default TableSampleDiscounts diff --git a/frontend/src/components/Discounts/configureDiscountsCols.tsx b/frontend/src/components/Discounts/configureDiscountsCols.tsx new file mode 100644 index 0000000..6f68155 --- /dev/null +++ b/frontend/src/components/Discounts/configureDiscountsCols.tsx @@ -0,0 +1,181 @@ +import React from 'react'; +import BaseIcon from '../BaseIcon'; +import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js'; +import axios from 'axios'; +import { + GridActionsCellItem, + GridRowParams, + GridValueGetterParams, +} from '@mui/x-data-grid'; +import ImageField from '../ImageField'; +import {saveFile} from "../../helpers/fileSaver"; +import dataFormatter from '../../helpers/dataFormatter' +import DataGridMultiSelect from "../DataGridMultiSelect"; +import ListActionsPopover from '../ListActionsPopover'; + +import {hasPermission} from "../../helpers/userPermissions"; + +type Params = (id: string) => void; + +export const loadColumns = async ( + onDelete: Params, + entityName: string, + + user + +) => { + async function callOptionsApi(entityName: string) { + + if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return []; + + try { + const data = await axios(`/${entityName}/autocomplete?limit=100`); + return data.data; + } catch (error) { + console.log(error); + return []; + } + } + + const hasUpdatePermission = hasPermission(user, 'UPDATE_DISCOUNTS') + + return [ + + { + field: 'code', + headerName: 'Code', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'description', + headerName: 'Description', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'type', + headerName: 'Type', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'value', + headerName: 'Value', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + type: 'number', + + }, + + { + field: 'active', + headerName: 'Active', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + type: 'boolean', + + }, + + { + field: 'starts', + headerName: 'StartsAt', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + type: 'dateTime', + valueGetter: (params: GridValueGetterParams) => + new Date(params.row.starts), + + }, + + { + field: 'ends', + headerName: 'EndsAt', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + type: 'dateTime', + valueGetter: (params: GridValueGetterParams) => + new Date(params.row.ends), + + }, + + { + field: 'actions', + type: 'actions', + minWidth: 30, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + getActions: (params: GridRowParams) => { + + return [ +
+ +
, + ] + }, + }, + ]; +}; diff --git a/frontend/src/components/FormField.tsx b/frontend/src/components/FormField.tsx index 7c35454..988ac39 100644 --- a/frontend/src/components/FormField.tsx +++ b/frontend/src/components/FormField.tsx @@ -38,9 +38,9 @@ const FormField = ({ icons = [], ...props }: Props) => { `${focusRing}`, props.hasTextareaHeight ? 'h-24' : 'h-12', props.isBorderless ? 'border-0' : 'border', - props.isTransparent ? 'bg-transparent' : `${props.websiteBg ? ` bg-skyBlueTheme-websiteBG ` : bgColor} dark:bg-dark-800`, + props.isTransparent ? 'bg-transparent' : `${props.websiteBg ? ` bg-white` : bgColor} dark:bg-dark-800`, props.disabled ? 'bg-gray-200 text-gray-100 dark:bg-dark-900 disabled' : '', - props.borderButtom ? `border-0 border-b ${props.diversity ? "placeholder-primaryText border-primaryText" : " placeholder-white border-white "} rounded-none focus:ring-0` : '', + props.borderButtom ? `border-0 border-b ${props.diversity ? "border-gray-400" : " placeholder-white border-gray-300/10 border-white "} rounded-none focus:ring-0` : '', ].join(' '); return ( diff --git a/frontend/src/components/FormImagePicker.tsx b/frontend/src/components/FormImagePicker.tsx index 06733d4..7bc34ae 100644 --- a/frontend/src/components/FormImagePicker.tsx +++ b/frontend/src/components/FormImagePicker.tsx @@ -77,7 +77,7 @@ const FormImagePicker = ({ label, icon, accept, color, isRoundIcon, path, schema /> {showFilename && !loading && ( -
+
{file.name} diff --git a/frontend/src/components/ImageField.tsx b/frontend/src/components/ImageField.tsx index 883f342..7ad5b55 100644 --- a/frontend/src/components/ImageField.tsx +++ b/frontend/src/components/ImageField.tsx @@ -34,7 +34,7 @@ export default function ImageField({ className={`rounded-full block h-auto w-full max-w-full bg-gray-100 dark:bg-dark-900 ${imageClassName}`} /> ) : ( -
+
void; - currentPage: number; - numPages: number; - onPageChange: (page: number) => void; -}; - -const CardInventory = ({ - inventory, - loading, - onDelete, - currentPage, - numPages, - onPageChange, -}: Props) => { - const asideScrollbarsStyle = useAppSelector( - (state) => state.style.asideScrollbarsStyle, - ); - const bgColor = useAppSelector((state) => state.style.cardsColor); - const darkMode = useAppSelector((state) => state.style.darkMode); - const corners = useAppSelector((state) => state.style.corners); - const focusRing = useAppSelector((state) => state.style.focusRingColor); - - const currentUser = useAppSelector((state) => state.auth.currentUser); - const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_INVENTORY') - - - return ( -
- {loading && } -
    - {!loading && inventory.map((item, index) => ( -
  • - -
    - - - {item.location} - - - -
    - -
    -
    -
    - -
    -
  • - ))} - {!loading && inventory.length === 0 && ( -
    -

    No data to display

    -
    - )} -
-
- -
-
- ); -}; - -export default CardInventory; diff --git a/frontend/src/components/Inventory_movements/CardInventory_movements.tsx b/frontend/src/components/Inventory_movements/CardInventory_movements.tsx new file mode 100644 index 0000000..18fcd43 --- /dev/null +++ b/frontend/src/components/Inventory_movements/CardInventory_movements.tsx @@ -0,0 +1,159 @@ +import React from 'react'; +import ImageField from '../ImageField'; +import ListActionsPopover from '../ListActionsPopover'; +import { useAppSelector } from '../../stores/hooks'; +import dataFormatter from '../../helpers/dataFormatter'; +import { Pagination } from '../Pagination'; +import {saveFile} from "../../helpers/fileSaver"; +import LoadingSpinner from "../LoadingSpinner"; +import Link from 'next/link'; + +import {hasPermission} from "../../helpers/userPermissions"; + + +type Props = { + inventory_movements: any[]; + loading: boolean; + onDelete: (id: string) => void; + currentPage: number; + numPages: number; + onPageChange: (page: number) => void; +}; + +const CardInventory_movements = ({ + inventory_movements, + loading, + onDelete, + currentPage, + numPages, + onPageChange, +}: Props) => { + const asideScrollbarsStyle = useAppSelector( + (state) => state.style.asideScrollbarsStyle, + ); + const bgColor = useAppSelector((state) => state.style.cardsColor); + const darkMode = useAppSelector((state) => state.style.darkMode); + const corners = useAppSelector((state) => state.style.corners); + const focusRing = useAppSelector((state) => state.style.focusRingColor); + + const currentUser = useAppSelector((state) => state.auth.currentUser); + const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_INVENTORY_MOVEMENTS') + + + return ( +
+ {loading && } +
    + {!loading && inventory_movements.map((item, index) => ( +
  • + +
    + + + {item.note} + + + +
    + +
    +
    +
    + + +
    +
    Note
    +
    +
    + { item.note } +
    +
    +
    + + + + +
    +
    Product
    +
    +
    + { dataFormatter.productsOneListFormatter(item.product) } +
    +
    +
    + + + + +
    +
    QuantityChange
    +
    +
    + { item.change } +
    +
    +
    + + + + +
    +
    Reason
    +
    +
    + { item.reason } +
    +
    +
    + + + + +
    +
    Date
    +
    +
    + { dataFormatter.dateTimeFormatter(item.date) } +
    +
    +
    + + + +
    +
  • + ))} + {!loading && inventory_movements.length === 0 && ( +
    +

    No data to display

    +
    + )} +
+
+ +
+
+ ); +}; + +export default CardInventory_movements; diff --git a/frontend/src/components/Inventory_movements/ListInventory_movements.tsx b/frontend/src/components/Inventory_movements/ListInventory_movements.tsx new file mode 100644 index 0000000..de39b5a --- /dev/null +++ b/frontend/src/components/Inventory_movements/ListInventory_movements.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import CardBox from '../CardBox'; +import ImageField from '../ImageField'; +import dataFormatter from '../../helpers/dataFormatter'; +import {saveFile} from "../../helpers/fileSaver"; +import ListActionsPopover from "../ListActionsPopover"; +import {useAppSelector} from "../../stores/hooks"; +import {Pagination} from "../Pagination"; +import LoadingSpinner from "../LoadingSpinner"; +import Link from 'next/link'; + +import {hasPermission} from "../../helpers/userPermissions"; + + +type Props = { + inventory_movements: any[]; + loading: boolean; + onDelete: (id: string) => void; + currentPage: number; + numPages: number; + onPageChange: (page: number) => void; +}; + +const ListInventory_movements = ({ inventory_movements, loading, onDelete, currentPage, numPages, onPageChange }: Props) => { + + const currentUser = useAppSelector((state) => state.auth.currentUser); + const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_INVENTORY_MOVEMENTS') + + const corners = useAppSelector((state) => state.style.corners); + const bgColor = useAppSelector((state) => state.style.cardsColor); + + + return ( + <> +
+ {loading && } + {!loading && inventory_movements.map((item) => ( +
+ +
+ + dark:divide-dark-700 overflow-x-auto' + } + > + + +
+

Note

+

{ item.note }

+
+ + + + +
+

Product

+

{ dataFormatter.productsOneListFormatter(item.product) }

+
+ + + + +
+

QuantityChange

+

{ item.change }

+
+ + + + +
+

Reason

+

{ item.reason }

+
+ + + + +
+

Date

+

{ dataFormatter.dateTimeFormatter(item.date) }

+
+ + + + + +
+
+
+ ))} + {!loading && inventory_movements.length === 0 && ( +
+

No data to display

+
+ )} +
+
+ +
+ + ) +}; + +export default ListInventory_movements \ No newline at end of file diff --git a/frontend/src/components/Inventory_movements/TableInventory_movements.tsx b/frontend/src/components/Inventory_movements/TableInventory_movements.tsx new file mode 100644 index 0000000..abe881c --- /dev/null +++ b/frontend/src/components/Inventory_movements/TableInventory_movements.tsx @@ -0,0 +1,476 @@ +import React, { useEffect, useState, useMemo } from 'react' +import { createPortal } from 'react-dom'; +import { ToastContainer, toast } from 'react-toastify'; +import BaseButton from '../BaseButton' +import CardBoxModal from '../CardBoxModal' +import CardBox from "../CardBox"; +import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/inventory_movements/inventory_movementsSlice' +import { useAppDispatch, useAppSelector } from '../../stores/hooks' +import { useRouter } from 'next/router' +import { Field, Form, Formik } from "formik"; +import { + DataGrid, + GridColDef, +} from '@mui/x-data-grid'; +import {loadColumns} from "./configureInventory_movementsCols"; +import _ from 'lodash'; +import dataFormatter from '../../helpers/dataFormatter' +import {dataGridStyles} from "../../styles"; + + +import ListInventory_movements from './ListInventory_movements'; + + +const perPage = 10 + +const TableSampleInventory_movements = ({ filterItems, setFilterItems, filters, showGrid }) => { + const notify = (type, msg) => toast( msg, {type, position: "bottom-center"}); + + const dispatch = useAppDispatch(); + const router = useRouter(); + + const pagesList = []; + const [id, setId] = useState(null); + const [currentPage, setCurrentPage] = useState(0); + const [filterRequest, setFilterRequest] = React.useState(''); + const [columns, setColumns] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); + const [sortModel, setSortModel] = useState([ + { + field: '', + sort: 'desc', + }, + ]); + + const { inventory_movements, loading, count, notify: inventory_movementsNotify, refetch } = useAppSelector((state) => state.inventory_movements) + const { currentUser } = useAppSelector((state) => state.auth); + const focusRing = useAppSelector((state) => state.style.focusRingColor); + const bgColor = useAppSelector((state) => state.style.bgLayoutColor); + const corners = useAppSelector((state) => state.style.corners); + const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage); + for (let i = 0; i < numPages; i++) { + pagesList.push(i); + } + + const loadData = async (page = currentPage, request = filterRequest) => { + if (page !== currentPage) setCurrentPage(page); + if (request !== filterRequest) setFilterRequest(request); + const { sort, field } = sortModel[0]; + + const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`; + dispatch(fetch({ limit: perPage, page, query })); + }; + + useEffect(() => { + if (inventory_movementsNotify.showNotification) { + notify(inventory_movementsNotify.typeNotification, inventory_movementsNotify.textNotification); + } + }, [inventory_movementsNotify.showNotification]); + + useEffect(() => { + if (!currentUser) return; + loadData(); + }, [sortModel, currentUser]); + + useEffect(() => { + if (refetch) { + loadData(0); + dispatch(setRefetch(false)); + } + }, [refetch, dispatch]); + + const [isModalInfoActive, setIsModalInfoActive] = useState(false) + const [isModalTrashActive, setIsModalTrashActive] = useState(false) + + const handleModalAction = () => { + setIsModalInfoActive(false) + setIsModalTrashActive(false) + } + + + + + + const handleDeleteModalAction = (id: string) => { + setId(id) + setIsModalTrashActive(true) + } + const handleDeleteAction = async () => { + if (id) { + await dispatch(deleteItem(id)); + await loadData(0); + setIsModalTrashActive(false); + } + }; + + const generateFilterRequests = useMemo(() => { + let request = '&'; + filterItems.forEach((item) => { + const isRangeFilter = filters.find( + (filter) => + filter.title === item.fields.selectedField && + (filter.number || filter.date), + ); + + if (isRangeFilter) { + const from = item.fields.filterValueFrom; + const to = item.fields.filterValueTo; + if (from) { + request += `${item.fields.selectedField}Range=${from}&`; + } + if (to) { + request += `${item.fields.selectedField}Range=${to}&`; + } + } else { + const value = item.fields.filterValue; + if (value) { + request += `${item.fields.selectedField}=${value}&`; + } + } + }); + return request; + }, [filterItems, filters]); + + const deleteFilter = (value) => { + const newItems = filterItems.filter((item) => item.id !== value); + + if (newItems.length) { + setFilterItems(newItems); + } else { + loadData(0, ''); + + setFilterItems(newItems); + } + }; + + const handleSubmit = () => { + loadData(0, generateFilterRequests); + + }; + + const handleChange = (id) => (e) => { + const value = e.target.value; + const name = e.target.name; + + setFilterItems( + filterItems.map((item) => { + if (item.id !== id) return item; + if (name === 'selectedField') return { id, fields: { [name]: value } }; + + return { id, fields: { ...item.fields, [name]: value } } + }), + ); + }; + + const handleReset = () => { + setFilterItems([]); + loadData(0, ''); + + }; + + const onPageChange = (page: number) => { + loadData(page); + setCurrentPage(page); + }; + + + useEffect(() => { + if (!currentUser) return; + + loadColumns( + handleDeleteModalAction, + `inventory_movements`, + currentUser, + ).then((newCols) => setColumns(newCols)); + }, [currentUser]); + + + + const handleTableSubmit = async (id: string, data) => { + + if (!_.isEmpty(data)) { + await dispatch(update({ id, data })) + .unwrap() + .then((res) => res) + .catch((err) => { + throw new Error(err); + }); + } + }; + + const onDeleteRows = async (selectedRows) => { + await dispatch(deleteItemsByIds(selectedRows)); + await loadData(0); + }; + + const controlClasses = + 'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' + + ` ${bgColor} ${focusRing} ${corners} ` + + 'dark:bg-slate-800 border'; + + + const dataGrid = ( +
+ `datagrid--row`} + rows={inventory_movements ?? []} + columns={columns} + initialState={{ + pagination: { + paginationModel: { + pageSize: 10, + }, + }, + }} + disableRowSelectionOnClick + onProcessRowUpdateError={(params) => { + console.log('Error', params); + }} + processRowUpdate={async (newRow, oldRow) => { + const data = dataFormatter.dataGridEditFormatter(newRow); + + try { + await handleTableSubmit(newRow.id, data); + return newRow; + } catch { + return oldRow; + } + }} + sortingMode={'server'} + checkboxSelection + onRowSelectionModelChange={(ids) => { + setSelectedRows(ids) + }} + onSortModelChange={(params) => { + params.length + ? setSortModel(params) + : setSortModel([{ field: '', sort: 'desc' }]); + }} + rowCount={count} + pageSizeOptions={[10]} + paginationMode={'server'} + loading={loading} + onPaginationModelChange={(params) => { + onPageChange(params.page); + }} + /> +
+ ) + + return ( + <> + {filterItems && Array.isArray( filterItems ) && filterItems.length ? + + null} + > +
+ <> + {filterItems && filterItems.map((filterItem) => { + return ( +
+
+
Filter
+ + {filters.map((selectOption) => ( + + ))} + +
+ {filters.find((filter) => + filter.title === filterItem?.fields?.selectedField + )?.type === 'enum' ? ( +
+
+ Value +
+ + + {filters.find((filter) => + filter.title === filterItem?.fields?.selectedField + )?.options?.map((option) => ( + + ))} + +
+ ) : filters.find((filter) => + filter.title === filterItem?.fields?.selectedField + )?.number ? ( +
+
+
From
+ +
+
+
To
+ +
+
+ ) : filters.find( + (filter) => + filter.title === + filterItem?.fields?.selectedField + )?.date ? ( +
+
+
+ From +
+ +
+
+
To
+ +
+
+ ) : ( +
+
Contains
+ +
+ )} +
+
Action
+ { + deleteFilter(filterItem.id) + }} + /> +
+
+ ) + })} +
+ + +
+ +
+
+
: null + } + +

Are you sure you want to delete this item?

+
+ + + {inventory_movements && Array.isArray(inventory_movements) && !showGrid && ( + + )} + + + + {showGrid && dataGrid} + + + {selectedRows.length > 0 && + createPortal( + onDeleteRows(selectedRows)} + />, + document.getElementById('delete-rows-button'), + )} + + + ) +} + +export default TableSampleInventory_movements diff --git a/frontend/src/components/Inventory_movements/configureInventory_movementsCols.tsx b/frontend/src/components/Inventory_movements/configureInventory_movementsCols.tsx new file mode 100644 index 0000000..484935f --- /dev/null +++ b/frontend/src/components/Inventory_movements/configureInventory_movementsCols.tsx @@ -0,0 +1,154 @@ +import React from 'react'; +import BaseIcon from '../BaseIcon'; +import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js'; +import axios from 'axios'; +import { + GridActionsCellItem, + GridRowParams, + GridValueGetterParams, +} from '@mui/x-data-grid'; +import ImageField from '../ImageField'; +import {saveFile} from "../../helpers/fileSaver"; +import dataFormatter from '../../helpers/dataFormatter' +import DataGridMultiSelect from "../DataGridMultiSelect"; +import ListActionsPopover from '../ListActionsPopover'; + +import {hasPermission} from "../../helpers/userPermissions"; + +type Params = (id: string) => void; + +export const loadColumns = async ( + onDelete: Params, + entityName: string, + + user + +) => { + async function callOptionsApi(entityName: string) { + + if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return []; + + try { + const data = await axios(`/${entityName}/autocomplete?limit=100`); + return data.data; + } catch (error) { + console.log(error); + return []; + } + } + + const hasUpdatePermission = hasPermission(user, 'UPDATE_INVENTORY_MOVEMENTS') + + return [ + + { + field: 'note', + headerName: 'Note', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'product', + headerName: 'Product', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + sortable: false, + type: 'singleSelect', + getOptionValue: (value: any) => value?.id, + getOptionLabel: (value: any) => value?.label, + valueOptions: await callOptionsApi('products'), + valueGetter: (params: GridValueGetterParams) => + params?.value?.id ?? params?.value, + + }, + + { + field: 'change', + headerName: 'QuantityChange', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + type: 'number', + + }, + + { + field: 'reason', + headerName: 'Reason', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + + }, + + { + field: 'date', + headerName: 'Date', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + + editable: hasUpdatePermission, + + type: 'dateTime', + valueGetter: (params: GridValueGetterParams) => + new Date(params.row.date), + + }, + + { + field: 'actions', + type: 'actions', + minWidth: 30, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + getActions: (params: GridRowParams) => { + + return [ +
+ +
, + ] + }, + }, + ]; +}; diff --git a/frontend/src/components/KanbanBoard/KanbanCard.tsx b/frontend/src/components/KanbanBoard/KanbanCard.tsx index 333bea7..7655572 100644 --- a/frontend/src/components/KanbanBoard/KanbanCard.tsx +++ b/frontend/src/components/KanbanBoard/KanbanCard.tsx @@ -34,7 +34,7 @@ const KanbanCard = ({
diff --git a/frontend/src/components/KanbanBoard/KanbanColumn.tsx b/frontend/src/components/KanbanBoard/KanbanColumn.tsx index dec0d7e..425a0d3 100644 --- a/frontend/src/components/KanbanBoard/KanbanColumn.tsx +++ b/frontend/src/components/KanbanBoard/KanbanColumn.tsx @@ -188,7 +188,7 @@ const KanbanColumn = ({
))} {!data?.length && ( -

No data

+

No data

)}
diff --git a/frontend/src/components/ListActionsPopover.tsx b/frontend/src/components/ListActionsPopover.tsx index 1e7fc2f..0f93245 100644 --- a/frontend/src/components/ListActionsPopover.tsx +++ b/frontend/src/components/ListActionsPopover.tsx @@ -53,7 +53,7 @@ const ListActionsPopover = ({ size={'small'} > { className='w-12 h-12 rounded-full absolute border-4 border-solid border-gray-200 dark:border-slate-800' >
diff --git a/frontend/src/components/NavBar.tsx b/frontend/src/components/NavBar.tsx index cedc7a7..c270ae0 100644 --- a/frontend/src/components/NavBar.tsx +++ b/frontend/src/components/NavBar.tsx @@ -37,7 +37,7 @@ export default function NavBar({ menu, className = '', children }: Props) {