Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e9432e89e |
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
|
/backend/node_modules
|
||||||
|
/frontend/node_modules
|
||||||
node_modules/
|
node_modules/
|
||||||
*/node_modules/
|
*/node_modules/
|
||||||
*/build/
|
**/node_modules/
|
||||||
|
*/build/
|
||||||
4
502.html
4
502.html
@ -129,8 +129,8 @@
|
|||||||
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
||||||
available.</p>
|
available.</p>
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<h2>StoreOps Manager</h2>
|
<h2>Store Operations Manager</h2>
|
||||||
<p>Manage products, inventory, orders, customers, and payments with Admin and Staff roles.</p>
|
<p>Store Operations Manager for catalog and full order lifecycle with products, customers, payments, and reporting.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="loader-container">
|
<div class="loader-container">
|
||||||
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
# StoreOps Manager
|
# Store Operations Manager
|
||||||
|
|
||||||
|
|
||||||
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
|
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
DB_NAME=app_37379
|
DB_NAME=app_37384
|
||||||
DB_USER=app_37379
|
DB_USER=app_37384
|
||||||
DB_PASS=fc0de3ba-4aee-4507-88c3-64c04c521f22
|
DB_PASS=4df3d09c-78bc-4451-8952-4e97b44f02af
|
||||||
DB_HOST=127.0.0.1
|
DB_HOST=127.0.0.1
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
#StoreOps Manager - template backend,
|
#Store Operations Manager - template backend,
|
||||||
|
|
||||||
#### Run App on local machine:
|
#### Run App on local machine:
|
||||||
|
|
||||||
@ -30,10 +30,10 @@
|
|||||||
- `psql postgres -U admin`
|
- `psql postgres -U admin`
|
||||||
|
|
||||||
- Type this command to creating a new database.
|
- Type this command to creating a new database.
|
||||||
- `postgres=> 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`.
|
- 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`
|
- `postgres=> \q`
|
||||||
|
|
||||||
------------
|
------------
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "storeopsmanager",
|
"name": "storeoperationsmanager",
|
||||||
"description": "StoreOps Manager - template backend",
|
"description": "Store Operations Manager - template backend",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
||||||
"db:migrate": "sequelize-cli db:migrate",
|
"db:migrate": "sequelize-cli db:migrate",
|
||||||
|
|||||||
@ -11,15 +11,15 @@ const config = {
|
|||||||
bcrypt: {
|
bcrypt: {
|
||||||
saltRounds: 12
|
saltRounds: 12
|
||||||
},
|
},
|
||||||
admin_pass: "fc0de3ba",
|
admin_pass: "4df3d09c",
|
||||||
user_pass: "64c04c521f22",
|
user_pass: "4e97b44f02af",
|
||||||
admin_email: "admin@flatlogic.com",
|
admin_email: "admin@flatlogic.com",
|
||||||
providers: {
|
providers: {
|
||||||
LOCAL: 'local',
|
LOCAL: 'local',
|
||||||
GOOGLE: 'google',
|
GOOGLE: 'google',
|
||||||
MICROSOFT: 'microsoft'
|
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: '',
|
remote: '',
|
||||||
port: process.env.NODE_ENV === "production" ? "" : "8080",
|
port: process.env.NODE_ENV === "production" ? "" : "8080",
|
||||||
hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost",
|
hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost",
|
||||||
@ -39,7 +39,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
uploadDir: os.tmpdir(),
|
uploadDir: os.tmpdir(),
|
||||||
email: {
|
email: {
|
||||||
from: 'StoreOps Manager <app@flatlogic.app>',
|
from: 'Store Operations Manager <app@flatlogic.app>',
|
||||||
host: 'email-smtp.us-east-1.amazonaws.com',
|
host: 'email-smtp.us-east-1.amazonaws.com',
|
||||||
port: 587,
|
port: 587,
|
||||||
auth: {
|
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',
|
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.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.host = process.env.NODE_ENV === "production" ? config.remote : "http://localhost";
|
||||||
config.apiUrl = `${config.host}${config.port ? `:${config.port}` : ``}/api`;
|
config.apiUrl = `${config.host}${config.port ? `:${config.port}` : ``}/api`;
|
||||||
config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`;
|
config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`;
|
||||||
|
|||||||
@ -195,6 +195,8 @@ module.exports = class CategoriesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.products_category = await categories.getProducts_category({
|
output.products_category = await categories.getProducts_category({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
@ -208,6 +210,7 @@ module.exports = class CategoriesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.parent = await categories.getParent({
|
output.parent = await categories.getParent({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,12 +36,12 @@ module.exports = class CustomersDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
billing_address: data.billing_address
|
company: data.company
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
shipping_address: data.shipping_address
|
address: data.address
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -51,10 +51,9 @@ module.exports = class CustomersDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
vip: data.vip
|
loyalty_points: data.loyalty_points
|
||||||
||
|
||
|
||||||
false
|
null
|
||||||
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: data.importHash || null,
|
importHash: data.importHash || null,
|
||||||
@ -97,12 +96,12 @@ module.exports = class CustomersDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
billing_address: item.billing_address
|
company: item.company
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
shipping_address: item.shipping_address
|
address: item.address
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -112,10 +111,9 @@ module.exports = class CustomersDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
vip: item.vip
|
loyalty_points: item.loyalty_points
|
||||||
||
|
||
|
||||||
false
|
null
|
||||||
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -154,16 +152,16 @@ module.exports = class CustomersDBApi {
|
|||||||
if (data.phone !== undefined) updatePayload.phone = data.phone;
|
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.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;
|
updatePayload.updatedById = currentUser.id;
|
||||||
@ -252,6 +250,9 @@ module.exports = class CustomersDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.orders_customer = await customers.getOrders_customer({
|
output.orders_customer = await customers.getOrders_customer({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
@ -333,24 +334,24 @@ module.exports = class CustomersDBApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.billing_address) {
|
if (filter.company) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'customers',
|
'customers',
|
||||||
'billing_address',
|
'company',
|
||||||
filter.billing_address,
|
filter.company,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.shipping_address) {
|
if (filter.address) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'customers',
|
'customers',
|
||||||
'shipping_address',
|
'address',
|
||||||
filter.shipping_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) {
|
if (filter.active !== undefined) {
|
||||||
where = {
|
where = {
|
||||||
@ -380,13 +405,6 @@ module.exports = class CustomersDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.vip) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
vip: filter.vip,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
519
backend/src/db/api/discounts.js
Normal file
519
backend/src/db/api/discounts.js
Normal file
@ -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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
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 currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const inventory = await db.inventory.create(
|
const inventory_movements = await db.inventory_movements.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
quantity: data.quantity
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
location: data.location
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
change_type: data.change_type
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
note: data.note
|
note: data.note
|
||||||
||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
change: data.change
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
reason: data.reason
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
date: data.date
|
||||||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ module.exports = class InventoryDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await inventory.setVariant( data.variant || null, {
|
await inventory_movements.setProduct( data.product || null, {
|
||||||
transaction,
|
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;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// 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,
|
id: item.id || undefined,
|
||||||
|
|
||||||
quantity: item.quantity
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
location: item.location
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
change_type: item.change_type
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
note: item.note
|
note: item.note
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
change: item.change
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
reason: item.reason
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
date: item.date
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -97,12 +97,12 @@ module.exports = class InventoryDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// 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
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
return inventory;
|
return inventory_movements;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -110,35 +110,35 @@ module.exports = class InventoryDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
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 = {};
|
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.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;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await inventory.update(updatePayload, {transaction});
|
await inventory_movements.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.variant !== undefined) {
|
if (data.product !== undefined) {
|
||||||
await inventory.setVariant(
|
await inventory_movements.setProduct(
|
||||||
|
|
||||||
data.variant,
|
data.product,
|
||||||
|
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
@ -150,14 +150,14 @@ module.exports = class InventoryDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return inventory;
|
return inventory_movements;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const inventory = await db.inventory.findAll({
|
const inventory_movements = await db.inventory_movements.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -167,53 +167,53 @@ module.exports = class InventoryDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of inventory) {
|
for (const record of inventory_movements) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of inventory) {
|
for (const record of inventory_movements) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return inventory;
|
return inventory_movements;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
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
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await inventory.destroy({
|
await inventory_movements.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return inventory;
|
return inventory_movements;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const inventory = await db.inventory.findOne(
|
const inventory_movements = await db.inventory_movements.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!inventory) {
|
if (!inventory_movements) {
|
||||||
return inventory;
|
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
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -261,15 +264,15 @@ module.exports = class InventoryDBApi {
|
|||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.product_variants,
|
model: db.products,
|
||||||
as: 'variant',
|
as: 'product',
|
||||||
|
|
||||||
where: filter.variant ? {
|
where: filter.product ? {
|
||||||
[Op.or]: [
|
[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: {
|
name: {
|
||||||
[Op.or]: filter.variant.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[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) {
|
if (filter.note) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'inventory',
|
'inventory_movements',
|
||||||
'note',
|
'note',
|
||||||
filter.note,
|
filter.note,
|
||||||
),
|
),
|
||||||
@ -317,14 +309,14 @@ module.exports = class InventoryDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.quantityRange) {
|
if (filter.changeRange) {
|
||||||
const [start, end] = filter.quantityRange;
|
const [start, end] = filter.changeRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
quantity: {
|
change: {
|
||||||
...where.quantity,
|
...where.change,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -333,8 +325,32 @@ module.exports = class InventoryDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
quantity: {
|
change: {
|
||||||
...where.quantity,
|
...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,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -350,10 +366,10 @@ module.exports = class InventoryDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.change_type) {
|
if (filter.reason) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
change_type: filter.change_type,
|
reason: filter.reason,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +424,7 @@ module.exports = class InventoryDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.inventory.findAndCountAll(queryOptions);
|
const { rows, count } = await db.inventory_movements.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -430,25 +446,25 @@ module.exports = class InventoryDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'inventory',
|
'inventory_movements',
|
||||||
'location',
|
'note',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.inventory.findAll({
|
const records = await db.inventory_movements.findAll({
|
||||||
attributes: [ 'id', 'location' ],
|
attributes: [ 'id', 'note' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['location', 'ASC']],
|
orderBy: [['note', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.location,
|
label: record.note,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ module.exports = class Order_itemsDBApi {
|
|||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
name: data.name
|
product_name: data.product_name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -38,6 +38,11 @@ module.exports = class Order_itemsDBApi {
|
|||||||
|
|
||||||
total: data.total
|
total: data.total
|
||||||
||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
sku: data.sku
|
||||||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
@ -49,11 +54,7 @@ module.exports = class Order_itemsDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await order_items.setOrder( data.order || null, {
|
await order_items.setProduct( data.product || null, {
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await order_items.setProduct_variant( data.product_variant || null, {
|
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ module.exports = class Order_itemsDBApi {
|
|||||||
const order_itemsData = data.map((item, index) => ({
|
const order_itemsData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
name: item.name
|
product_name: item.product_name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -92,6 +93,11 @@ module.exports = class Order_itemsDBApi {
|
|||||||
total: item.total
|
total: item.total
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
sku: item.sku
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -121,7 +127,7 @@ module.exports = class Order_itemsDBApi {
|
|||||||
|
|
||||||
const updatePayload = {};
|
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;
|
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.total !== undefined) updatePayload.total = data.total;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.sku !== undefined) updatePayload.sku = data.sku;
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await order_items.update(updatePayload, {transaction});
|
await order_items.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.order !== undefined) {
|
if (data.product !== undefined) {
|
||||||
await order_items.setOrder(
|
await order_items.setProduct(
|
||||||
|
|
||||||
data.order,
|
data.product,
|
||||||
|
|
||||||
{ transaction }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.product_variant !== undefined) {
|
|
||||||
await order_items.setProduct_variant(
|
|
||||||
|
|
||||||
data.product_variant,
|
|
||||||
|
|
||||||
{ transaction }
|
{ 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
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -279,32 +277,15 @@ module.exports = class Order_itemsDBApi {
|
|||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.orders,
|
model: db.products,
|
||||||
as: 'order',
|
as: 'product',
|
||||||
|
|
||||||
where: filter.order ? {
|
where: filter.product ? {
|
||||||
[Op.or]: [
|
[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: {
|
name: {
|
||||||
[Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[Op.or]: filter.product.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}%` }))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -325,13 +306,24 @@ module.exports = class Order_itemsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.name) {
|
if (filter.product_name) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'order_items',
|
'order_items',
|
||||||
'name',
|
'product_name',
|
||||||
filter.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) {
|
if (filter.createdAtRange) {
|
||||||
@ -498,7 +488,7 @@ module.exports = class Order_itemsDBApi {
|
|||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'order_items',
|
'order_items',
|
||||||
'name',
|
'product_name',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -506,16 +496,16 @@ module.exports = class Order_itemsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.order_items.findAll({
|
const records = await db.order_items.findAll({
|
||||||
attributes: [ 'id', 'name' ],
|
attributes: [ 'id', 'product_name' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['name', 'ASC']],
|
orderBy: [['product_name', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.name,
|
label: record.product_name,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,28 +31,43 @@ module.exports = class OrdersDBApi {
|
|||||||
null
|
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
|
total: data.total
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
placed_at: data.placed_at
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
shipped_at: data.shipped_at
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
payment_status: data.payment_status
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
shipping_address: data.shipping_address
|
shipping_address: data.shipping_address
|
||||||
||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
billing_address: data.billing_address
|
||||||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
@ -68,8 +83,24 @@ module.exports = class OrdersDBApi {
|
|||||||
transaction,
|
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
|
status: item.status
|
||||||
||
|
||
|
||||||
null
|
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
|
total: item.total
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
|
||||||
|
|
||||||
placed_at: item.placed_at
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
shipped_at: item.shipped_at
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
payment_status: item.payment_status
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
,
|
||||||
|
|
||||||
shipping_address: item.shipping_address
|
shipping_address: item.shipping_address
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
billing_address: item.billing_address
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -153,21 +199,30 @@ module.exports = class OrdersDBApi {
|
|||||||
if (data.status !== undefined) updatePayload.status = data.status;
|
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.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.shipping_address !== undefined) updatePayload.shipping_address = data.shipping_address;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.billing_address !== undefined) updatePayload.billing_address = data.billing_address;
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await orders.update(updatePayload, {transaction});
|
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
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
output.payments_order = await orders.getPayments_order({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.shipments_order = await orders.getShipments_order({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.customer = await orders.getCustomer({
|
output.customer = await orders.getCustomer({
|
||||||
transaction
|
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;
|
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) {
|
if (filter.totalRange) {
|
||||||
const [start, end] = 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) {
|
if (filter.active !== undefined) {
|
||||||
where = {
|
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) {
|
if (filter.createdAtRange) {
|
||||||
|
|||||||
@ -21,12 +21,7 @@ module.exports = class PaymentsDBApi {
|
|||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
transaction_id: data.transaction_id
|
transaction_ref: data.transaction_ref
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
payment_method: data.payment_method
|
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -36,6 +31,11 @@ module.exports = class PaymentsDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
|
method: data.method
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
status: data.status
|
status: data.status
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
@ -46,6 +46,17 @@ module.exports = class PaymentsDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
|
provider: data.provider
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
refunded: data.refunded
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
importHash: data.importHash || null,
|
importHash: data.importHash || null,
|
||||||
createdById: currentUser.id,
|
createdById: currentUser.id,
|
||||||
updatedById: 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;
|
return payments;
|
||||||
}
|
}
|
||||||
@ -75,12 +92,7 @@ module.exports = class PaymentsDBApi {
|
|||||||
const paymentsData = data.map((item, index) => ({
|
const paymentsData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
transaction_id: item.transaction_id
|
transaction_ref: item.transaction_ref
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
payment_method: item.payment_method
|
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -88,6 +100,11 @@ module.exports = class PaymentsDBApi {
|
|||||||
amount: item.amount
|
amount: item.amount
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
method: item.method
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
status: item.status
|
status: item.status
|
||||||
@ -98,6 +115,17 @@ module.exports = class PaymentsDBApi {
|
|||||||
paid_at: item.paid_at
|
paid_at: item.paid_at
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
provider: item.provider
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
refunded: item.refunded
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -111,6 +139,18 @@ module.exports = class PaymentsDBApi {
|
|||||||
|
|
||||||
// For each item created, replace relation files
|
// 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;
|
return payments;
|
||||||
}
|
}
|
||||||
@ -127,41 +167,48 @@ module.exports = class PaymentsDBApi {
|
|||||||
|
|
||||||
const updatePayload = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
if (data.transaction_id !== undefined) updatePayload.transaction_id = data.transaction_id;
|
if (data.transaction_ref !== undefined) updatePayload.transaction_ref = data.transaction_ref;
|
||||||
|
|
||||||
|
|
||||||
if (data.payment_method !== undefined) updatePayload.payment_method = data.payment_method;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.amount !== undefined) updatePayload.amount = data.amount;
|
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.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
if (data.paid_at !== undefined) updatePayload.paid_at = data.paid_at;
|
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;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await payments.update(updatePayload, {transaction});
|
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;
|
return payments;
|
||||||
}
|
}
|
||||||
@ -243,7 +290,10 @@ module.exports = class PaymentsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.order = await payments.getOrder({
|
|
||||||
|
|
||||||
|
|
||||||
|
output.receipt = await payments.getReceipt({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -273,25 +323,13 @@ module.exports = class PaymentsDBApi {
|
|||||||
|
|
||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.orders,
|
model: db.file,
|
||||||
as: 'order',
|
as: 'receipt',
|
||||||
|
|
||||||
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) {
|
||||||
@ -303,13 +341,24 @@ module.exports = class PaymentsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.transaction_id) {
|
if (filter.transaction_ref) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'payments',
|
'payments',
|
||||||
'transaction_id',
|
'transaction_ref',
|
||||||
filter.transaction_id,
|
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 = {
|
||||||
...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) {
|
if (filter.createdAtRange) {
|
||||||
@ -464,7 +518,7 @@ module.exports = class PaymentsDBApi {
|
|||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'payments',
|
'payments',
|
||||||
'transaction_id',
|
'transaction_ref',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -472,16 +526,16 @@ module.exports = class PaymentsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.payments.findAll({
|
const records = await db.payments.findAll({
|
||||||
attributes: [ 'id', 'transaction_id' ],
|
attributes: [ 'id', 'transaction_ref' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['transaction_id', 'ASC']],
|
orderBy: [['transaction_ref', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.transaction_id,
|
label: record.transaction_ref,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -178,6 +178,9 @@ module.exports = class PermissionsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,17 @@ module.exports = class ProductsDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
weight: data.weight
|
cost: data.cost
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
currency: data.currency
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
stock: data.stock
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -64,8 +74,16 @@ module.exports = class ProductsDBApi {
|
|||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await products.setSupplier( data.supplier || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await products.setTags(data.tags || [], {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
await FileDBApi.replaceRelationFiles(
|
||||||
@ -111,7 +129,17 @@ module.exports = class ProductsDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
weight: item.weight
|
cost: item.cost
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
currency: item.currency
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
stock: item.stock
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -173,7 +201,13 @@ module.exports = class ProductsDBApi {
|
|||||||
if (data.price !== undefined) updatePayload.price = data.price;
|
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;
|
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(
|
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
|
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;
|
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) {
|
if (filter.costRange) {
|
||||||
const [start, end] = filter.weightRange;
|
const [start, end] = filter.costRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
weight: {
|
cost: {
|
||||||
...where.weight,
|
...where.cost,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -442,8 +544,32 @@ module.exports = class ProductsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
weight: {
|
cost: {
|
||||||
...where.weight,
|
...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,
|
[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) {
|
if (filter.createdAtRange) {
|
||||||
|
|||||||
552
backend/src/db/api/returns.js
Normal file
552
backend/src/db/api/returns.js
Normal file
@ -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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
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 currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const product_variants = await db.product_variants.create(
|
const reviews = await db.reviews.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
sku: data.sku
|
author_name: data.author_name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
title: data.title
|
rating: data.rating
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
price: data.price
|
comment: data.comment
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
stock: data.stock
|
created: data.created
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
attributes: data.attributes
|
approved: data.approved
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
active: data.active
|
|
||||||
||
|
||
|
||||||
false
|
false
|
||||||
|
|
||||||
@ -60,11 +55,7 @@ module.exports = class Product_variantsDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await product_variants.setProduct( data.product || null, {
|
await reviews.setProduct( data.product || null, {
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await product_variants.setSupplier( data.supplier || null, {
|
|
||||||
transaction,
|
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;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// 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,
|
id: item.id || undefined,
|
||||||
|
|
||||||
sku: item.sku
|
author_name: item.author_name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
title: item.title
|
rating: item.rating
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
price: item.price
|
comment: item.comment
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
stock: item.stock
|
created: item.created
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
attributes: item.attributes
|
approved: item.approved
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
active: item.active
|
|
||||||
||
|
||
|
||||||
false
|
false
|
||||||
|
|
||||||
@ -133,24 +109,12 @@ module.exports = class Product_variantsDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// 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 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) {
|
static async update(id, data, options) {
|
||||||
@ -158,39 +122,36 @@ module.exports = class Product_variantsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
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 = {};
|
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.approved !== undefined) updatePayload.approved = data.approved;
|
||||||
|
|
||||||
|
|
||||||
if (data.active !== undefined) updatePayload.active = data.active;
|
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await product_variants.update(updatePayload, {transaction});
|
await reviews.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.product !== undefined) {
|
if (data.product !== undefined) {
|
||||||
await product_variants.setProduct(
|
await reviews.setProduct(
|
||||||
|
|
||||||
data.product,
|
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) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const product_variants = await db.product_variants.findAll({
|
const reviews = await db.reviews.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -240,53 +182,53 @@ module.exports = class Product_variantsDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of product_variants) {
|
for (const record of reviews) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of product_variants) {
|
for (const record of reviews) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return product_variants;
|
return reviews;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
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
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await product_variants.destroy({
|
await reviews.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return product_variants;
|
return reviews;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const product_variants = await db.product_variants.findOne(
|
const reviews = await db.reviews.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!product_variants) {
|
if (!reviews) {
|
||||||
return product_variants;
|
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
|
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) {
|
if (filter) {
|
||||||
@ -403,35 +308,24 @@ module.exports = class Product_variantsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.sku) {
|
if (filter.author_name) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'product_variants',
|
'reviews',
|
||||||
'sku',
|
'author_name',
|
||||||
filter.sku,
|
filter.author_name,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.title) {
|
if (filter.comment) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'product_variants',
|
'reviews',
|
||||||
'title',
|
'comment',
|
||||||
filter.title,
|
filter.comment,
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.attributes) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'product_variants',
|
|
||||||
'attributes',
|
|
||||||
filter.attributes,
|
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -441,14 +335,14 @@ module.exports = class Product_variantsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.priceRange) {
|
if (filter.ratingRange) {
|
||||||
const [start, end] = filter.priceRange;
|
const [start, end] = filter.ratingRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
price: {
|
rating: {
|
||||||
...where.price,
|
...where.rating,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -457,22 +351,22 @@ module.exports = class Product_variantsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
price: {
|
rating: {
|
||||||
...where.price,
|
...where.rating,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.stockRange) {
|
if (filter.createdRange) {
|
||||||
const [start, end] = filter.stockRange;
|
const [start, end] = filter.createdRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
stock: {
|
created: {
|
||||||
...where.stock,
|
...where.created,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -481,8 +375,8 @@ module.exports = class Product_variantsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
stock: {
|
created: {
|
||||||
...where.stock,
|
...where.created,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -498,10 +392,10 @@ module.exports = class Product_variantsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.active) {
|
if (filter.approved) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
active: filter.active,
|
approved: filter.approved,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,8 +403,6 @@ module.exports = class Product_variantsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.createdAtRange) {
|
if (filter.createdAtRange) {
|
||||||
@ -558,7 +450,7 @@ module.exports = class Product_variantsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.product_variants.findAndCountAll(queryOptions);
|
const { rows, count } = await db.reviews.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -580,25 +472,25 @@ module.exports = class Product_variantsDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'product_variants',
|
'reviews',
|
||||||
'sku',
|
'author_name',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.product_variants.findAll({
|
const records = await db.reviews.findAll({
|
||||||
attributes: [ 'id', 'sku' ],
|
attributes: [ 'id', 'author_name' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['sku', 'ASC']],
|
orderBy: [['author_name', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.sku,
|
label: record.author_name,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +203,9 @@ module.exports = class RolesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.permissions = await roles.getPermissions({
|
output.permissions = await roles.getPermissions({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,12 +21,12 @@ module.exports = class ShipmentsDBApi {
|
|||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
carrier: data.carrier
|
tracking_number: data.tracking_number
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
tracking_number: data.tracking_number
|
carrier: data.carrier
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -46,7 +46,7 @@ module.exports = class ShipmentsDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
cost: data.cost
|
notes: data.notes
|
||||||
||
|
||
|
||||||
null
|
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) => ({
|
const shipmentsData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
carrier: item.carrier
|
tracking_number: item.tracking_number
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
tracking_number: item.tracking_number
|
carrier: item.carrier
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -105,7 +101,7 @@ module.exports = class ShipmentsDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
cost: item.cost
|
notes: item.notes
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -137,12 +133,12 @@ module.exports = class ShipmentsDBApi {
|
|||||||
|
|
||||||
const updatePayload = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
if (data.carrier !== undefined) updatePayload.carrier = data.carrier;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.tracking_number !== undefined) updatePayload.tracking_number = data.tracking_number;
|
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;
|
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.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;
|
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 = [
|
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) {
|
if (filter.carrier) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
@ -327,13 +306,13 @@ module.exports = class ShipmentsDBApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.tracking_number) {
|
if (filter.notes) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'shipments',
|
'shipments',
|
||||||
'tracking_number',
|
'notes',
|
||||||
filter.tracking_number,
|
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) {
|
if (filter.active !== undefined) {
|
||||||
where = {
|
where = {
|
||||||
@ -451,8 +406,6 @@ module.exports = class ShipmentsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.createdAtRange) {
|
if (filter.createdAtRange) {
|
||||||
|
|||||||
@ -31,12 +31,12 @@ module.exports = class SuppliersDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
phone: data.phone
|
email: data.email
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
email: data.email
|
phone: data.phone
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -86,12 +86,12 @@ module.exports = class SuppliersDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
phone: item.phone
|
email: item.email
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
email: item.email
|
phone: item.phone
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -139,12 +139,12 @@ module.exports = class SuppliersDBApi {
|
|||||||
if (data.contact_name !== undefined) updatePayload.contact_name = data.contact_name;
|
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.email !== undefined) updatePayload.email = data.email;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.phone !== undefined) updatePayload.phone = data.phone;
|
||||||
|
|
||||||
|
|
||||||
if (data.address !== undefined) updatePayload.address = data.address;
|
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
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -247,6 +248,8 @@ module.exports = class SuppliersDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
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) {
|
if (filter.email) {
|
||||||
where = {
|
where = {
|
||||||
...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) {
|
if (filter.address) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
|
|||||||
366
backend/src/db/api/tags.js
Normal file
366
backend/src/db/api/tags.js
Normal file
@ -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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
@ -411,6 +411,13 @@ module.exports = class UsersDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.orders_assigned_to = await users.getOrders_assigned_to({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ module.exports = {
|
|||||||
username: 'postgres',
|
username: 'postgres',
|
||||||
dialect: 'postgres',
|
dialect: 'postgres',
|
||||||
password: '',
|
password: '',
|
||||||
database: 'db_storeops_manager',
|
database: 'db_store_operations_manager',
|
||||||
host: process.env.DB_HOST || 'localhost',
|
host: process.env.DB_HOST || 'localhost',
|
||||||
logging: console.log,
|
logging: console.log,
|
||||||
seederStorage: 'sequelize',
|
seederStorage: 'sequelize',
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -52,6 +52,8 @@ description: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.categories.hasMany(db.products, {
|
db.categories.hasMany(db.products, {
|
||||||
as: 'products_category',
|
as: 'products_category',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
@ -69,6 +71,7 @@ description: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -35,14 +35,14 @@ phone: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
billing_address: {
|
company: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
shipping_address: {
|
address: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
@ -56,11 +56,8 @@ notes: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
vip: {
|
loyalty_points: {
|
||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: false,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -93,6 +90,9 @@ vip: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.customers.hasMany(db.orders, {
|
db.customers.hasMany(db.orders, {
|
||||||
as: 'orders_customer',
|
as: 'orders_customer',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
|
|||||||
135
backend/src/db/models/discounts.js
Normal file
135
backend/src/db/models/discounts.js
Normal file
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const inventory = sequelize.define(
|
const inventory_movements = sequelize.define(
|
||||||
'inventory',
|
'inventory_movements',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,44 +14,44 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
quantity: {
|
note: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
change: {
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
location: {
|
reason: {
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
change_type: {
|
|
||||||
type: DataTypes.ENUM,
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
values: [
|
values: [
|
||||||
|
|
||||||
"addition",
|
"Purchase",
|
||||||
|
|
||||||
|
|
||||||
"deduction",
|
"Sale",
|
||||||
|
|
||||||
|
|
||||||
"adjustment",
|
"Return",
|
||||||
|
|
||||||
|
|
||||||
"transfer"
|
"Adjustment"
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
note: {
|
date: {
|
||||||
type: DataTypes.TEXT,
|
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
|
/// 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
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.inventory.belongsTo(db.product_variants, {
|
db.inventory_movements.belongsTo(db.products, {
|
||||||
as: 'variant',
|
as: 'product',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'variantId',
|
name: 'productId',
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
@ -105,18 +108,18 @@ note: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.inventory.belongsTo(db.users, {
|
db.inventory_movements.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.inventory.belongsTo(db.users, {
|
db.inventory_movements.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return inventory;
|
return inventory_movements;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
name: {
|
product_name: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
@ -40,6 +40,13 @@ total: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
sku: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
@ -75,22 +82,17 @@ total: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.order_items.belongsTo(db.orders, {
|
db.order_items.belongsTo(db.products, {
|
||||||
as: 'order',
|
as: 'product',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'orderId',
|
name: 'productId',
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
db.order_items.belongsTo(db.product_variants, {
|
|
||||||
as: 'product_variant',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'product_variantId',
|
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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: {
|
total: {
|
||||||
type: DataTypes.DECIMAL,
|
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: {
|
shipping_address: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
billing_address: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
@ -114,6 +120,60 @@ shipping_address: {
|
|||||||
|
|
||||||
orders.associate = (db) => {
|
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
|
/// 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: {
|
foreignKey: {
|
||||||
name: 'orderId',
|
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
|
//end loop
|
||||||
|
|
||||||
@ -168,6 +215,14 @@ shipping_address: {
|
|||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
db.orders.belongsTo(db.users, {
|
||||||
|
as: 'assigned_to',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'assigned_toId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,14 +14,21 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
transaction_id: {
|
transaction_ref: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
payment_method: {
|
amount: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
method: {
|
||||||
type: DataTypes.ENUM,
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
@ -43,13 +50,6 @@ payment_method: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
amount: {
|
|
||||||
type: DataTypes.DECIMAL,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
status: {
|
status: {
|
||||||
type: DataTypes.ENUM,
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
@ -57,15 +57,15 @@ status: {
|
|||||||
|
|
||||||
values: [
|
values: [
|
||||||
|
|
||||||
"Succeeded",
|
"Pending",
|
||||||
|
|
||||||
|
|
||||||
|
"Completed",
|
||||||
|
|
||||||
|
|
||||||
"Failed",
|
"Failed",
|
||||||
|
|
||||||
|
|
||||||
"Pending",
|
|
||||||
|
|
||||||
|
|
||||||
"Refunded"
|
"Refunded"
|
||||||
|
|
||||||
],
|
],
|
||||||
@ -77,6 +77,23 @@ paid_at: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
provider: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
refunded: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
@ -112,21 +129,26 @@ paid_at: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.payments.belongsTo(db.orders, {
|
|
||||||
as: 'order',
|
|
||||||
foreignKey: {
|
db.payments.hasMany(db.file, {
|
||||||
name: 'orderId',
|
as: 'receipt',
|
||||||
},
|
foreignKey: 'belongsToId',
|
||||||
constraints: false,
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
belongsTo: db.payments.getTableName(),
|
||||||
|
belongsToColumn: 'receipt',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.payments.belongsTo(db.users, {
|
db.payments.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -54,6 +54,9 @@ name: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -42,11 +42,25 @@ price: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
weight: {
|
cost: {
|
||||||
type: DataTypes.DECIMAL,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
currency: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
stock: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
active: {
|
active: {
|
||||||
@ -74,6 +88,24 @@ active: {
|
|||||||
|
|
||||||
products.associate = (db) => {
|
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
|
/// 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: {
|
foreignKey: {
|
||||||
name: 'productId',
|
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,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
db.products.belongsTo(db.suppliers, {
|
||||||
|
as: 'supplier',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'supplierId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.products.hasMany(db.file, {
|
db.products.hasMany(db.file, {
|
||||||
|
|||||||
139
backend/src/db/models/returns.js
Normal file
139
backend/src/db/models/returns.js
Normal file
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const product_variants = sequelize.define(
|
const reviews = sequelize.define(
|
||||||
'product_variants',
|
'reviews',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,42 +14,35 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
sku: {
|
author_name: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
title: {
|
rating: {
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
price: {
|
|
||||||
type: DataTypes.DECIMAL,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
stock: {
|
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
attributes: {
|
comment: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
active: {
|
created: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
approved: {
|
||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.BOOLEAN,
|
||||||
|
|
||||||
allowNull: false,
|
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
|
/// 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',
|
as: 'product',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'productId',
|
name: 'productId',
|
||||||
@ -120,39 +100,21 @@ active: {
|
|||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
db.product_variants.belongsTo(db.suppliers, {
|
|
||||||
as: 'supplier',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'supplierId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.product_variants.hasMany(db.file, {
|
db.reviews.belongsTo(db.users, {
|
||||||
as: 'images',
|
|
||||||
foreignKey: 'belongsToId',
|
|
||||||
constraints: false,
|
|
||||||
scope: {
|
|
||||||
belongsTo: db.product_variants.getTableName(),
|
|
||||||
belongsToColumn: 'images',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
db.product_variants.belongsTo(db.users, {
|
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.product_variants.belongsTo(db.users, {
|
db.reviews.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return product_variants;
|
return reviews;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -87,6 +87,9 @@ role_customization: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,14 +14,14 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
carrier: {
|
tracking_number: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
tracking_number: {
|
carrier: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ status: {
|
|||||||
|
|
||||||
values: [
|
values: [
|
||||||
|
|
||||||
"Pending",
|
"LabelCreated",
|
||||||
|
|
||||||
|
|
||||||
"InTransit",
|
"InTransit",
|
||||||
@ -64,8 +64,8 @@ delivered_at: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
cost: {
|
notes: {
|
||||||
type: DataTypes.DECIMAL,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -104,18 +104,13 @@ cost: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.shipments.belongsTo(db.orders, {
|
|
||||||
as: 'order',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'orderId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -28,14 +28,14 @@ contact_name: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
phone: {
|
email: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
email: {
|
phone: {
|
||||||
type: DataTypes.TEXT,
|
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: {
|
foreignKey: {
|
||||||
name: 'supplierId',
|
name: 'supplierId',
|
||||||
},
|
},
|
||||||
@ -97,6 +98,8 @@ notes: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
88
backend/src/db/models/tags.js
Normal file
88
backend/src/db/models/tags.js
Normal file
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -152,6 +152,17 @@ provider: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.users.hasMany(db.orders, {
|
||||||
|
as: 'orders_assigned_to',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'assigned_toId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@ const authRoutes = require('./routes/auth');
|
|||||||
const fileRoutes = require('./routes/file');
|
const fileRoutes = require('./routes/file');
|
||||||
const searchRoutes = require('./routes/search');
|
const searchRoutes = require('./routes/search');
|
||||||
const pexelsRoutes = require('./routes/pexels');
|
const pexelsRoutes = require('./routes/pexels');
|
||||||
|
const dashboardRoutes = require('./routes/dashboard');
|
||||||
|
|
||||||
const openaiRoutes = require('./routes/openai');
|
const openaiRoutes = require('./routes/openai');
|
||||||
|
|
||||||
@ -30,11 +31,17 @@ const customersRoutes = require('./routes/customers');
|
|||||||
|
|
||||||
const categoriesRoutes = require('./routes/categories');
|
const categoriesRoutes = require('./routes/categories');
|
||||||
|
|
||||||
|
const suppliersRoutes = require('./routes/suppliers');
|
||||||
|
|
||||||
|
const tagsRoutes = require('./routes/tags');
|
||||||
|
|
||||||
const productsRoutes = require('./routes/products');
|
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');
|
const ordersRoutes = require('./routes/orders');
|
||||||
|
|
||||||
@ -44,7 +51,7 @@ const paymentsRoutes = require('./routes/payments');
|
|||||||
|
|
||||||
const shipmentsRoutes = require('./routes/shipments');
|
const shipmentsRoutes = require('./routes/shipments');
|
||||||
|
|
||||||
const suppliersRoutes = require('./routes/suppliers');
|
const returnsRoutes = require('./routes/returns');
|
||||||
|
|
||||||
|
|
||||||
const getBaseUrl = (url) => {
|
const getBaseUrl = (url) => {
|
||||||
@ -57,8 +64,8 @@ const options = {
|
|||||||
openapi: "3.0.0",
|
openapi: "3.0.0",
|
||||||
info: {
|
info: {
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
title: "StoreOps Manager",
|
title: "Store Operations 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.",
|
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: [
|
servers: [
|
||||||
{
|
{
|
||||||
@ -101,6 +108,7 @@ app.use(bodyParser.json());
|
|||||||
app.use('/api/auth', authRoutes);
|
app.use('/api/auth', authRoutes);
|
||||||
app.use('/api/file', fileRoutes);
|
app.use('/api/file', fileRoutes);
|
||||||
app.use('/api/pexels', pexelsRoutes);
|
app.use('/api/pexels', pexelsRoutes);
|
||||||
|
app.use('/api/dashboard', passport.authenticate('jwt', {session: false}), dashboardRoutes);
|
||||||
app.enable('trust proxy');
|
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/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/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);
|
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/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(
|
app.use(
|
||||||
'/api/openai',
|
'/api/openai',
|
||||||
|
|||||||
@ -26,6 +26,12 @@ router.use(checkCrudPermissions('categories'));
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
* description:
|
||||||
|
* type: string
|
||||||
|
* default: description
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -285,7 +291,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','name','description',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,28 @@ router.use(checkCrudPermissions('customers'));
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* 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 }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','name','email','phone','company','address','notes',
|
||||||
|
'loyalty_points',
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
60
backend/src/routes/dashboard.js
Normal file
60
backend/src/routes/dashboard.js
Normal file
@ -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' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const Product_variantsService = require('../services/product_variants');
|
const DiscountsService = require('../services/discounts');
|
||||||
const Product_variantsDBApi = require('../db/api/product_variants');
|
const DiscountsDBApi = require('../db/api/discounts');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,36 +15,46 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('product_variants'));
|
router.use(checkCrudPermissions('discounts'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Product_variants:
|
* Discounts:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
|
* code:
|
||||||
|
* type: string
|
||||||
|
* default: code
|
||||||
|
* description:
|
||||||
|
* type: string
|
||||||
|
* default: description
|
||||||
|
|
||||||
|
|
||||||
|
* value:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Product_variants
|
* name: Discounts
|
||||||
* description: The Product_variants managing API
|
* description: The Discounts managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/product_variants:
|
* /api/discounts:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -56,14 +66,14 @@ router.use(checkCrudPermissions('product_variants'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -74,7 +84,7 @@ router.use(checkCrudPermissions('product_variants'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -85,7 +95,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -98,14 +108,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -117,18 +127,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/product_variants/{id}:
|
* /api/discounts/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -151,7 +161,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -160,7 +170,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -171,18 +181,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/product_variants/{id}:
|
* /api/discounts/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -198,7 +208,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -209,18 +219,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/product_variants/deleteByIds:
|
* /api/discounts/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -238,7 +248,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -247,29 +257,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/product_variants:
|
* /api/discounts:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Get all product_variants
|
* summary: Get all discounts
|
||||||
* description: Get all product_variants
|
* description: Get all discounts
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Product_variants list successfully received
|
* description: Discounts list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -281,14 +291,14 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await Product_variantsDBApi.findAll(
|
const payload = await DiscountsDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','code','description',
|
||||||
|
|
||||||
|
'value',
|
||||||
|
'starts','ends',
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
@ -307,22 +317,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/product_variants/count:
|
* /api/discounts/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Count all product_variants
|
* summary: Count all discounts
|
||||||
* description: Count all product_variants
|
* description: Count all discounts
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Product_variants count successfully received
|
* description: Discounts count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -333,7 +343,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await Product_variantsDBApi.findAll(
|
const payload = await DiscountsDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -344,22 +354,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/product_variants/autocomplete:
|
* /api/discounts/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Find all product_variants that match search criteria
|
* summary: Find all discounts that match search criteria
|
||||||
* description: Find all product_variants that match search criteria
|
* description: Find all discounts that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Product_variants list successfully received
|
* description: Discounts list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -369,7 +379,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', 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.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -381,11 +391,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/product_variants/{id}:
|
* /api/discounts/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Product_variants]
|
* tags: [Discounts]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -401,7 +411,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Product_variants"
|
* $ref: "#/components/schemas/Discounts"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -412,7 +422,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await Product_variantsDBApi.findBy(
|
const payload = await DiscountsDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
433
backend/src/routes/inventory_movements.js
Normal file
433
backend/src/routes/inventory_movements.js
Normal file
@ -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;
|
||||||
@ -26,8 +26,23 @@ router.use(checkCrudPermissions('order_items'));
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* 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 }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','product_name','sku',
|
||||||
|
'quantity',
|
||||||
|
'unit_price','total',
|
||||||
|
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
|
|||||||
@ -26,9 +26,31 @@ router.use(checkCrudPermissions('orders'));
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* 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 }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
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 };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -26,9 +26,20 @@ router.use(checkCrudPermissions('payments'));
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* 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 }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','transaction_ref','provider',
|
||||||
|
|
||||||
|
'amount',
|
||||||
|
'paid_at',
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -26,8 +26,29 @@ router.use(checkCrudPermissions('products'));
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* 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 }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','name','sku','description','currency',
|
||||||
|
'stock',
|
||||||
|
'price','cost',
|
||||||
|
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
|
|||||||
436
backend/src/routes/returns.js
Normal file
436
backend/src/routes/returns.js
Normal file
@ -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;
|
||||||
435
backend/src/routes/reviews.js
Normal file
435
backend/src/routes/reviews.js
Normal file
@ -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;
|
||||||
@ -26,9 +26,19 @@ router.use(checkCrudPermissions('shipments'));
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* 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 }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','tracking_number','carrier','notes',
|
||||||
|
|
||||||
|
|
||||||
|
'shipped_at','delivered_at',
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -26,6 +26,24 @@ router.use(checkCrudPermissions('suppliers'));
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* 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 }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','name','contact_name','email','phone','address','notes',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const InventoryService = require('../services/inventory');
|
const TagsService = require('../services/tags');
|
||||||
const InventoryDBApi = require('../db/api/inventory');
|
const TagsDBApi = require('../db/api/tags');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,17 +15,23 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('inventory'));
|
router.use(checkCrudPermissions('tags'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Inventory:
|
* Tags:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
* color:
|
||||||
|
* type: string
|
||||||
|
* default: color
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -34,17 +40,17 @@ router.use(checkCrudPermissions('inventory'));
|
|||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Inventory
|
* name: Tags
|
||||||
* description: The Inventory managing API
|
* description: The Tags managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/inventory:
|
* /api/tags:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -56,14 +62,14 @@ router.use(checkCrudPermissions('inventory'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -74,7 +80,7 @@ router.use(checkCrudPermissions('inventory'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -85,7 +91,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -98,14 +104,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -117,18 +123,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await InventoryService.bulkImport(req, res, true, link.host);
|
await TagsService.bulkImport(req, res, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/inventory/{id}:
|
* /api/tags/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -151,7 +157,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -160,7 +166,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -171,18 +177,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/inventory/{id}:
|
* /api/tags/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -198,7 +204,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -209,18 +215,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/inventory/deleteByIds:
|
* /api/tags/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -238,7 +244,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -247,29 +253,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/inventory:
|
* /api/tags:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Get all inventory
|
* summary: Get all tags
|
||||||
* description: Get all inventory
|
* description: Get all tags
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Inventory list successfully received
|
* description: Tags list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -281,11 +287,11 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await InventoryDBApi.findAll(
|
const payload = await TagsDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id',
|
const fields = ['id','name','color',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -307,22 +313,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/inventory/count:
|
* /api/tags/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Count all inventory
|
* summary: Count all tags
|
||||||
* description: Count all inventory
|
* description: Count all tags
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Inventory count successfully received
|
* description: Tags count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -333,7 +339,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await InventoryDBApi.findAll(
|
const payload = await TagsDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -344,22 +350,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/inventory/autocomplete:
|
* /api/tags/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Find all inventory that match search criteria
|
* summary: Find all tags that match search criteria
|
||||||
* description: Find all inventory that match search criteria
|
* description: Find all tags that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Inventory list successfully received
|
* description: Tags list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -369,7 +375,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', async (req, res) => {
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
const payload = await InventoryDBApi.findAllAutocomplete(
|
const payload = await TagsDBApi.findAllAutocomplete(
|
||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -381,11 +387,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/inventory/{id}:
|
* /api/tags/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Inventory]
|
* tags: [Tags]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -401,7 +407,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Inventory"
|
* $ref: "#/components/schemas/Tags"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -412,7 +418,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await InventoryDBApi.findBy(
|
const payload = await TagsDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const db = require('../db/models');
|
const db = require('../db/models');
|
||||||
const InventoryDBApi = require('../db/api/inventory');
|
const DiscountsDBApi = require('../db/api/discounts');
|
||||||
const processFile = require("../middlewares/upload");
|
const processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
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) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
await InventoryDBApi.create(
|
await DiscountsDBApi.create(
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -51,7 +51,7 @@ module.exports = class InventoryService {
|
|||||||
.on('error', (error) => reject(error));
|
.on('error', (error) => reject(error));
|
||||||
})
|
})
|
||||||
|
|
||||||
await InventoryDBApi.bulkImport(results, {
|
await DiscountsDBApi.bulkImport(results, {
|
||||||
transaction,
|
transaction,
|
||||||
ignoreDuplicates: true,
|
ignoreDuplicates: true,
|
||||||
validate: true,
|
validate: true,
|
||||||
@ -68,18 +68,18 @@ module.exports = class InventoryService {
|
|||||||
static async update(data, id, currentUser) {
|
static async update(data, id, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
let inventory = await InventoryDBApi.findBy(
|
let discounts = await DiscountsDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{transaction},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!inventory) {
|
if (!discounts) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'inventoryNotFound',
|
'discountsNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedInventory = await InventoryDBApi.update(
|
const updatedDiscounts = await DiscountsDBApi.update(
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ module.exports = class InventoryService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
return updatedInventory;
|
return updatedDiscounts;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@ -101,7 +101,7 @@ module.exports = class InventoryService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await InventoryDBApi.deleteByIds(ids, {
|
await DiscountsDBApi.deleteByIds(ids, {
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ module.exports = class InventoryService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await InventoryDBApi.remove(
|
await DiscountsDBApi.remove(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
138
backend/src/services/inventory_movements.js
Normal file
138
backend/src/services/inventory_movements.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
const errors = {
|
const errors = {
|
||||||
app: {
|
app: {
|
||||||
title: 'StoreOps Manager',
|
title: 'Store Operations Manager',
|
||||||
},
|
},
|
||||||
|
|
||||||
auth: {
|
auth: {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
const db = require('../db/models');
|
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 processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
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) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
await Product_variantsDBApi.create(
|
await ReturnsDBApi.create(
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -51,7 +51,7 @@ module.exports = class Product_variantsService {
|
|||||||
.on('error', (error) => reject(error));
|
.on('error', (error) => reject(error));
|
||||||
})
|
})
|
||||||
|
|
||||||
await Product_variantsDBApi.bulkImport(results, {
|
await ReturnsDBApi.bulkImport(results, {
|
||||||
transaction,
|
transaction,
|
||||||
ignoreDuplicates: true,
|
ignoreDuplicates: true,
|
||||||
validate: true,
|
validate: true,
|
||||||
@ -68,18 +68,18 @@ module.exports = class Product_variantsService {
|
|||||||
static async update(data, id, currentUser) {
|
static async update(data, id, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
let product_variants = await Product_variantsDBApi.findBy(
|
let returns = await ReturnsDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{transaction},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!product_variants) {
|
if (!returns) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'product_variantsNotFound',
|
'returnsNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedProduct_variants = await Product_variantsDBApi.update(
|
const updatedReturns = await ReturnsDBApi.update(
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ module.exports = class Product_variantsService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
return updatedProduct_variants;
|
return updatedReturns;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@ -101,7 +101,7 @@ module.exports = class Product_variantsService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Product_variantsDBApi.deleteByIds(ids, {
|
await ReturnsDBApi.deleteByIds(ids, {
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ module.exports = class Product_variantsService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Product_variantsDBApi.remove(
|
await ReturnsDBApi.remove(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
138
backend/src/services/reviews.js
Normal file
138
backend/src/services/reviews.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -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 = {
|
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",
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
138
backend/src/services/tags.js
Normal file
138
backend/src/services/tags.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ services:
|
|||||||
- ./data/db:/var/lib/postgresql/data
|
- ./data/db:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||||
- POSTGRES_DB=db_storeops_manager
|
- POSTGRES_DB=db_store_operations_manager
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
logging:
|
logging:
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# StoreOps Manager
|
# Store Operations Manager
|
||||||
|
|
||||||
## This project was generated by Flatlogic Platform.
|
## This project was generated by Flatlogic Platform.
|
||||||
## Install
|
## Install
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ColorButtonKey } from './interfaces'
|
import type { ColorButtonKey } from './interfaces'
|
||||||
|
|
||||||
export const gradientBgBase = 'bg-gradient-to-tr'
|
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 gradientBgPurplePink = `${gradientBgBase} from-purple-400 via-pink-500 to-red-500`
|
||||||
export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}`
|
export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}`
|
||||||
export const gradientBgDark = `${gradientBgBase} from-dark-700 via-dark-900 to-dark-800`;
|
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 = {
|
export const colorsBgLight = {
|
||||||
white: 'bg-white text-black',
|
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',
|
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',
|
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',
|
danger: 'bg-red-500 border-red-500 text-white',
|
||||||
@ -19,7 +19,7 @@ export const colorsBgLight = {
|
|||||||
|
|
||||||
export const colorsText = {
|
export const colorsText = {
|
||||||
white: 'text-black dark:text-slate-100',
|
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',
|
contrast: 'dark:text-white',
|
||||||
success: 'text-emerald-500',
|
success: 'text-emerald-500',
|
||||||
danger: 'text-red-500',
|
danger: 'text-red-500',
|
||||||
@ -50,13 +50,13 @@ export const getButtonColor = (
|
|||||||
const colors = {
|
const colors = {
|
||||||
ring: {
|
ring: {
|
||||||
white: 'ring-gray-200 dark:ring-gray-500',
|
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',
|
lightDark: 'ring-gray-200 dark:ring-gray-500',
|
||||||
contrast: 'ring-gray-300 dark:ring-gray-400',
|
contrast: 'ring-gray-300 dark:ring-gray-400',
|
||||||
success: 'ring-emerald-300 dark:ring-pavitra-blue',
|
success: 'ring-emerald-300 dark:ring-pavitra-blue',
|
||||||
danger: 'ring-red-300 dark:ring-red-700',
|
danger: 'ring-red-300 dark:ring-red-700',
|
||||||
warning: 'ring-yellow-300 dark:ring-yellow-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: {
|
active: {
|
||||||
white: 'bg-gray-100',
|
white: 'bg-gray-100',
|
||||||
@ -66,21 +66,21 @@ export const getButtonColor = (
|
|||||||
success: 'bg-emerald-700 dark:bg-pavitra-blue',
|
success: 'bg-emerald-700 dark:bg-pavitra-blue',
|
||||||
danger: 'bg-red-700 dark:bg-red-600',
|
danger: 'bg-red-700 dark:bg-red-600',
|
||||||
warning: 'bg-yellow-700 dark:bg-yellow-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: {
|
bg: {
|
||||||
white: 'bg-white text-black',
|
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',
|
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',
|
contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black',
|
||||||
success: 'bg-emerald-600 dark:bg-pavitra-blue text-white',
|
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',
|
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: {
|
bgHover: {
|
||||||
white: 'hover:bg-gray-100',
|
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',
|
lightDark: 'hover:bg-gray-200 hover:dark:bg-slate-700',
|
||||||
contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100',
|
contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100',
|
||||||
success:
|
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',
|
'hover:bg-red-700 hover:border-red-700 hover:dark:bg-red-600 hover:dark:border-red-600',
|
||||||
warning:
|
warning:
|
||||||
'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600',
|
'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: {
|
borders: {
|
||||||
white: 'border-white',
|
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',
|
lightDark: 'border-gray-100 dark:border-slate-800',
|
||||||
contrast: 'border-gray-800 dark:border-white',
|
contrast: 'border-gray-800 dark:border-white',
|
||||||
success: 'border-emerald-600 dark:border-pavitra-blue',
|
success: 'border-emerald-600 dark:border-pavitra-blue',
|
||||||
danger: 'border-red-600 dark:border-red-500',
|
danger: 'border-red-600 dark:border-red-500',
|
||||||
warning: 'border-yellow-600 dark:border-yellow-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: {
|
text: {
|
||||||
contrast: 'dark:text-slate-100',
|
contrast: 'dark:text-slate-100',
|
||||||
success: 'text-emerald-600 dark:text-pavitra-blue',
|
success: 'text-emerald-600 dark:text-pavitra-blue',
|
||||||
danger: 'text-red-600 dark:text-red-500',
|
danger: 'text-red-600 dark:text-red-500',
|
||||||
warning: 'text-yellow-600 dark:text-yellow-500',
|
warning: 'text-yellow-600 dark:text-yellow-500',
|
||||||
info: ' dark:text-pavitra-blue',
|
info: 'text-blue-600 dark:text-pavitra-blue',
|
||||||
},
|
},
|
||||||
outlineHover: {
|
outlineHover: {
|
||||||
contrast:
|
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',
|
'hover:bg-red-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-red-600',
|
||||||
warning:
|
warning:
|
||||||
'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600',
|
'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",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props
|
|||||||
>
|
>
|
||||||
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0">
|
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0">
|
||||||
|
|
||||||
<b className="font-black">StoreOps Manager</b>
|
<b className="font-black">Store Operations Manager</b>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -76,6 +76,42 @@ const CardCategories = ({
|
|||||||
</div>
|
</div>
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>CategoryName</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.name }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Description</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.description }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>ParentCategory</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.categoriesOneListFormatter(item.parent) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const ListCategories = ({ categories, loading, onDelete, currentPage, numPages,
|
|||||||
{!loading && categories.map((item) => (
|
{!loading && categories.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex ${bgColor} ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/categories/categories-view/?id=${item.id}`}
|
href={`/categories/categories-view/?id=${item.id}`}
|
||||||
@ -46,6 +46,30 @@ const ListCategories = ({ categories, loading, onDelete, currentPage, numPages,
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>CategoryName</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.name }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Description</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.description }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>ParentCategory</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.categoriesOneListFormatter(item.parent) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import dataFormatter from '../../helpers/dataFormatter'
|
|||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
|
|
||||||
|
|
||||||
import CardCategories from './CardCategories';
|
import ListCategories from './ListCategories';
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
const perPage = 10
|
||||||
@ -414,13 +414,13 @@ const TableSampleCategories = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2 mr-3"
|
className="my-2 mr-3"
|
||||||
type='submit' color='info'
|
color="success"
|
||||||
label='Apply'
|
label='Apply'
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2"
|
className="my-2"
|
||||||
type='reset' color='info' outline
|
color='info'
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
/>
|
/>
|
||||||
@ -443,7 +443,7 @@ const TableSampleCategories = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
|
|
||||||
|
|
||||||
{categories && Array.isArray(categories) && !showGrid && (
|
{categories && Array.isArray(categories) && !showGrid && (
|
||||||
<CardCategories
|
<ListCategories
|
||||||
categories={categories}
|
categories={categories}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onDelete={handleDeleteModalAction}
|
onDelete={handleDeleteModalAction}
|
||||||
|
|||||||
@ -41,6 +41,58 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
headerName: 'CategoryName',
|
||||||
|
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: 'parent',
|
||||||
|
headerName: 'ParentCategory',
|
||||||
|
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('categories'),
|
||||||
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
|
params?.value?.id ?? params?.value,
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
|
|||||||
@ -76,6 +76,90 @@ const CardCustomers = ({
|
|||||||
</div>
|
</div>
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>CustomerName</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.name }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Email</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.email }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Phone</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.phone }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Company</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.company }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Address</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.address }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Notes</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.notes }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>LoyaltyPoints</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.loyalty_points }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const ListCustomers = ({ customers, loading, onDelete, currentPage, numPages, on
|
|||||||
{!loading && customers.map((item) => (
|
{!loading && customers.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex ${bgColor} ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/customers/customers-view/?id=${item.id}`}
|
href={`/customers/customers-view/?id=${item.id}`}
|
||||||
@ -46,6 +46,62 @@ const ListCustomers = ({ customers, loading, onDelete, currentPage, numPages, on
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>CustomerName</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.name }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Email</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.email }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Phone</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.phone }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Company</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.company }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Address</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.address }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Notes</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.notes }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>LoyaltyPoints</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.loyalty_points }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
|
|||||||
@ -18,8 +18,6 @@ import dataFormatter from '../../helpers/dataFormatter'
|
|||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
|
|
||||||
|
|
||||||
import ListCustomers from './ListCustomers';
|
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
const perPage = 10
|
||||||
|
|
||||||
@ -414,13 +412,13 @@ const TableSampleCustomers = ({ filterItems, setFilterItems, filters, showGrid }
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2 mr-3"
|
className="my-2 mr-3"
|
||||||
type='submit' color='info'
|
color="success"
|
||||||
label='Apply'
|
label='Apply'
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2"
|
className="my-2"
|
||||||
type='reset' color='info' outline
|
color='info'
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
/>
|
/>
|
||||||
@ -442,21 +440,10 @@ const TableSampleCustomers = ({ filterItems, setFilterItems, filters, showGrid }
|
|||||||
</CardBoxModal>
|
</CardBoxModal>
|
||||||
|
|
||||||
|
|
||||||
{customers && Array.isArray(customers) && !showGrid && (
|
{dataGrid}
|
||||||
<ListCustomers
|
|
||||||
customers={customers}
|
|
||||||
loading={loading}
|
|
||||||
onDelete={handleDeleteModalAction}
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
onPageChange={onPageChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{showGrid && dataGrid}
|
|
||||||
|
|
||||||
|
|
||||||
{selectedRows.length > 0 &&
|
{selectedRows.length > 0 &&
|
||||||
createPortal(
|
createPortal(
|
||||||
|
|||||||
@ -41,6 +41,112 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
return [
|
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',
|
field: 'actions',
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
|
|||||||
183
frontend/src/components/Discounts/CardDiscounts.tsx
Normal file
183
frontend/src/components/Discounts/CardDiscounts.tsx
Normal file
@ -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 (
|
||||||
|
<div className={'p-4'}>
|
||||||
|
{loading && <LoadingSpinner />}
|
||||||
|
<ul
|
||||||
|
role='list'
|
||||||
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
|
>
|
||||||
|
{!loading && discounts.map((item, index) => (
|
||||||
|
<li
|
||||||
|
key={item.id}
|
||||||
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
|
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
|
<Link href={`/discounts/discounts-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
|
{item.code}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='ml-auto '>
|
||||||
|
<ListActionsPopover
|
||||||
|
onDelete={onDelete}
|
||||||
|
itemId={item.id}
|
||||||
|
pathEdit={`/discounts/discounts-edit/?id=${item.id}`}
|
||||||
|
pathView={`/discounts/discounts-view/?id=${item.id}`}
|
||||||
|
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Code</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.code }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Description</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.description }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Type</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.type }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Value</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.value }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Active</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.booleanFormatter(item.active) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>StartsAt</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.dateTimeFormatter(item.starts) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>EndsAt</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.dateTimeFormatter(item.ends) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
{!loading && discounts.length === 0 && (
|
||||||
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
|
<p className=''>No data to display</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
<div className={'flex items-center justify-center my-6'}>
|
||||||
|
<Pagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
numPages={numPages}
|
||||||
|
setCurrentPage={onPageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CardDiscounts;
|
||||||
136
frontend/src/components/Discounts/ListDiscounts.tsx
Normal file
136
frontend/src/components/Discounts/ListDiscounts.tsx
Normal file
@ -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 (
|
||||||
|
<>
|
||||||
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
|
{loading && <LoadingSpinner />}
|
||||||
|
{!loading && discounts.map((item) => (
|
||||||
|
<div key={item.id}>
|
||||||
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={`/discounts/discounts-view/?id=${item.id}`}
|
||||||
|
className={
|
||||||
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Code</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.code }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Description</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.description }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Type</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.type }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Value</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.value }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Active</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.active) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>StartsAt</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.starts) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>EndsAt</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.ends) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Link>
|
||||||
|
<ListActionsPopover
|
||||||
|
onDelete={onDelete}
|
||||||
|
itemId={item.id}
|
||||||
|
pathEdit={`/discounts/discounts-edit/?id=${item.id}`}
|
||||||
|
pathView={`/discounts/discounts-view/?id=${item.id}`}
|
||||||
|
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardBox>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{!loading && discounts.length === 0 && (
|
||||||
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
|
<p className=''>No data to display</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={'flex items-center justify-center my-6'}>
|
||||||
|
<Pagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
numPages={numPages}
|
||||||
|
setCurrentPage={onPageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListDiscounts
|
||||||
@ -4,7 +4,7 @@ import { ToastContainer, toast } from 'react-toastify';
|
|||||||
import BaseButton from '../BaseButton'
|
import BaseButton from '../BaseButton'
|
||||||
import CardBoxModal from '../CardBoxModal'
|
import CardBoxModal from '../CardBoxModal'
|
||||||
import CardBox from "../CardBox";
|
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 { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Field, Form, Formik } from "formik";
|
import { Field, Form, Formik } from "formik";
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
DataGrid,
|
DataGrid,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import {loadColumns} from "./configureProduct_variantsCols";
|
import {loadColumns} from "./configureDiscountsCols";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
@ -21,7 +21,7 @@ import {dataGridStyles} from "../../styles";
|
|||||||
|
|
||||||
const perPage = 10
|
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 notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
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 { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
@ -60,10 +60,10 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (product_variantsNotify.showNotification) {
|
if (discountsNotify.showNotification) {
|
||||||
notify(product_variantsNotify.typeNotification, product_variantsNotify.textNotification);
|
notify(discountsNotify.typeNotification, discountsNotify.textNotification);
|
||||||
}
|
}
|
||||||
}, [product_variantsNotify.showNotification]);
|
}, [discountsNotify.showNotification]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
@ -177,7 +177,7 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
|||||||
|
|
||||||
loadColumns(
|
loadColumns(
|
||||||
handleDeleteModalAction,
|
handleDeleteModalAction,
|
||||||
`product_variants`,
|
`discounts`,
|
||||||
currentUser,
|
currentUser,
|
||||||
).then((newCols) => setColumns(newCols));
|
).then((newCols) => setColumns(newCols));
|
||||||
}, [currentUser]);
|
}, [currentUser]);
|
||||||
@ -215,7 +215,7 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
|||||||
sx={dataGridStyles}
|
sx={dataGridStyles}
|
||||||
className={'datagrid--table'}
|
className={'datagrid--table'}
|
||||||
getRowClassName={() => `datagrid--row`}
|
getRowClassName={() => `datagrid--row`}
|
||||||
rows={product_variants ?? []}
|
rows={discounts ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -412,13 +412,13 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2 mr-3"
|
className="my-2 mr-3"
|
||||||
type='submit' color='info'
|
color="success"
|
||||||
label='Apply'
|
label='Apply'
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2"
|
className="my-2"
|
||||||
type='reset' color='info' outline
|
color='info'
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
/>
|
/>
|
||||||
@ -460,4 +460,4 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TableSampleProduct_variants
|
export default TableSampleDiscounts
|
||||||
181
frontend/src/components/Discounts/configureDiscountsCols.tsx
Normal file
181
frontend/src/components/Discounts/configureDiscountsCols.tsx
Normal file
@ -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 [
|
||||||
|
<div key={params?.row?.id}>
|
||||||
|
<ListActionsPopover
|
||||||
|
onDelete={onDelete}
|
||||||
|
itemId={params?.row?.id}
|
||||||
|
pathEdit={`/discounts/discounts-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/discounts/discounts-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
@ -38,9 +38,9 @@ const FormField = ({ icons = [], ...props }: Props) => {
|
|||||||
`${focusRing}`,
|
`${focusRing}`,
|
||||||
props.hasTextareaHeight ? 'h-24' : 'h-12',
|
props.hasTextareaHeight ? 'h-24' : 'h-12',
|
||||||
props.isBorderless ? 'border-0' : 'border',
|
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.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(' ');
|
].join(' ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -77,7 +77,7 @@ const FormImagePicker = ({ label, icon, accept, color, isRoundIcon, path, schema
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
{showFilename && !loading && (
|
{showFilename && !loading && (
|
||||||
<div className={` ${cornersRight} px-4 py-2 max-w-full flex-grow-0 overflow-x-hidden ${bgColor} dark:bg-slate-800 border-gray-200 dark:border-slate-700 `}>
|
<div className={` ${cornersRight} px-4 py-2 max-w-full flex-grow-0 overflow-x-hidden ${bgColor} dark:bg-slate-800 border-gray-200 dark:border-slate-700 border `}>
|
||||||
<span className='text-ellipsis max-w-full line-clamp-1'>
|
<span className='text-ellipsis max-w-full line-clamp-1'>
|
||||||
{file.name}
|
{file.name}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -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}`}
|
className={`rounded-full block h-auto w-full max-w-full bg-gray-100 dark:bg-dark-900 ${imageClassName}`}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className={'flex h-full dark:bg-dark-900/70'}>
|
<div className={'flex h-full bg-slate-100 dark:bg-dark-900/70'}>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
className='text-black dark:text-white'
|
className='text-black dark:text-white'
|
||||||
w='w-full'
|
w='w-full'
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
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: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => 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 (
|
|
||||||
<div className={'p-4'}>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
<ul
|
|
||||||
role='list'
|
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
|
||||||
>
|
|
||||||
{!loading && inventory.map((item, index) => (
|
|
||||||
<li
|
|
||||||
key={item.id}
|
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
|
||||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
|
||||||
|
|
||||||
<Link href={`/inventory/inventory-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
|
||||||
{item.location}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='ml-auto '>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/inventory/inventory-edit/?id=${item.id}`}
|
|
||||||
pathView={`/inventory/inventory-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{!loading && inventory.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardInventory;
|
|
||||||
@ -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 (
|
||||||
|
<div className={'p-4'}>
|
||||||
|
{loading && <LoadingSpinner />}
|
||||||
|
<ul
|
||||||
|
role='list'
|
||||||
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
|
>
|
||||||
|
{!loading && inventory_movements.map((item, index) => (
|
||||||
|
<li
|
||||||
|
key={item.id}
|
||||||
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
|
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
|
<Link href={`/inventory_movements/inventory_movements-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
|
{item.note}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='ml-auto '>
|
||||||
|
<ListActionsPopover
|
||||||
|
onDelete={onDelete}
|
||||||
|
itemId={item.id}
|
||||||
|
pathEdit={`/inventory_movements/inventory_movements-edit/?id=${item.id}`}
|
||||||
|
pathView={`/inventory_movements/inventory_movements-view/?id=${item.id}`}
|
||||||
|
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Note</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.note }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Product</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.productsOneListFormatter(item.product) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>QuantityChange</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.change }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Reason</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.reason }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Date</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.dateTimeFormatter(item.date) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
{!loading && inventory_movements.length === 0 && (
|
||||||
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
|
<p className=''>No data to display</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
<div className={'flex items-center justify-center my-6'}>
|
||||||
|
<Pagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
numPages={numPages}
|
||||||
|
setCurrentPage={onPageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CardInventory_movements;
|
||||||
@ -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 (
|
||||||
|
<>
|
||||||
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
|
{loading && <LoadingSpinner />}
|
||||||
|
{!loading && inventory_movements.map((item) => (
|
||||||
|
<div key={item.id}>
|
||||||
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={`/inventory_movements/inventory_movements-view/?id=${item.id}`}
|
||||||
|
className={
|
||||||
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Note</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.note }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Product</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.productsOneListFormatter(item.product) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>QuantityChange</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.change }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Reason</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.reason }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Date</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.date) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Link>
|
||||||
|
<ListActionsPopover
|
||||||
|
onDelete={onDelete}
|
||||||
|
itemId={item.id}
|
||||||
|
pathEdit={`/inventory_movements/inventory_movements-edit/?id=${item.id}`}
|
||||||
|
pathView={`/inventory_movements/inventory_movements-view/?id=${item.id}`}
|
||||||
|
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardBox>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{!loading && inventory_movements.length === 0 && (
|
||||||
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
|
<p className=''>No data to display</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={'flex items-center justify-center my-6'}>
|
||||||
|
<Pagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
numPages={numPages}
|
||||||
|
setCurrentPage={onPageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListInventory_movements
|
||||||
@ -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<GridColDef[]>([]);
|
||||||
|
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 = (
|
||||||
|
<div className='relative overflow-x-auto'>
|
||||||
|
<DataGrid
|
||||||
|
autoHeight
|
||||||
|
rowHeight={64}
|
||||||
|
sx={dataGridStyles}
|
||||||
|
className={'datagrid--table'}
|
||||||
|
getRowClassName={() => `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);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
||||||
|
<CardBox>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
checkboxes: ['lorem'],
|
||||||
|
switches: ['lorem'],
|
||||||
|
radio: 'lorem',
|
||||||
|
}}
|
||||||
|
onSubmit={() => null}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<>
|
||||||
|
{filterItems && filterItems.map((filterItem) => {
|
||||||
|
return (
|
||||||
|
<div key={filterItem.id} className="flex mb-4">
|
||||||
|
<div className="flex flex-col w-full mr-3">
|
||||||
|
<div className=" text-gray-500 font-bold">Filter</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='selectedField'
|
||||||
|
id='selectedField'
|
||||||
|
component='select'
|
||||||
|
value={filterItem?.fields?.selectedField || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
>
|
||||||
|
{filters.map((selectOption) => (
|
||||||
|
<option
|
||||||
|
key={selectOption.title}
|
||||||
|
value={`${selectOption.title}`}
|
||||||
|
>
|
||||||
|
{selectOption.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Field>
|
||||||
|
</div>
|
||||||
|
{filters.find((filter) =>
|
||||||
|
filter.title === filterItem?.fields?.selectedField
|
||||||
|
)?.type === 'enum' ? (
|
||||||
|
<div className="flex flex-col w-full mr-3">
|
||||||
|
<div className="text-gray-500 font-bold">
|
||||||
|
Value
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name="filterValue"
|
||||||
|
id='filterValue'
|
||||||
|
component="select"
|
||||||
|
value={filterItem?.fields?.filterValue || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
>
|
||||||
|
<option value="">Select Value</option>
|
||||||
|
{filters.find((filter) =>
|
||||||
|
filter.title === filterItem?.fields?.selectedField
|
||||||
|
)?.options?.map((option) => (
|
||||||
|
<option key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Field>
|
||||||
|
</div>
|
||||||
|
) : filters.find((filter) =>
|
||||||
|
filter.title === filterItem?.fields?.selectedField
|
||||||
|
)?.number ? (
|
||||||
|
<div className="flex flex-row w-full mr-3">
|
||||||
|
<div className="flex flex-col w-full mr-3">
|
||||||
|
<div className=" text-gray-500 font-bold">From</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValueFrom'
|
||||||
|
placeholder='From'
|
||||||
|
id='filterValueFrom'
|
||||||
|
value={filterItem?.fields?.filterValueFrom || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col w-full">
|
||||||
|
<div className=" text-gray-500 font-bold">To</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValueTo'
|
||||||
|
placeholder='to'
|
||||||
|
id='filterValueTo'
|
||||||
|
value={filterItem?.fields?.filterValueTo || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : filters.find(
|
||||||
|
(filter) =>
|
||||||
|
filter.title ===
|
||||||
|
filterItem?.fields?.selectedField
|
||||||
|
)?.date ? (
|
||||||
|
<div className='flex flex-row w-full mr-3'>
|
||||||
|
<div className='flex flex-col w-full mr-3'>
|
||||||
|
<div className=' text-gray-500 font-bold'>
|
||||||
|
From
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValueFrom'
|
||||||
|
placeholder='From'
|
||||||
|
id='filterValueFrom'
|
||||||
|
type='datetime-local'
|
||||||
|
value={filterItem?.fields?.filterValueFrom || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col w-full'>
|
||||||
|
<div className=' text-gray-500 font-bold'>To</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValueTo'
|
||||||
|
placeholder='to'
|
||||||
|
id='filterValueTo'
|
||||||
|
type='datetime-local'
|
||||||
|
value={filterItem?.fields?.filterValueTo || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col w-full mr-3">
|
||||||
|
<div className=" text-gray-500 font-bold">Contains</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValue'
|
||||||
|
placeholder='Contained'
|
||||||
|
id='filterValue'
|
||||||
|
value={filterItem?.fields?.filterValue || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className=" text-gray-500 font-bold">Action</div>
|
||||||
|
<BaseButton
|
||||||
|
className="my-2"
|
||||||
|
type='reset'
|
||||||
|
color='danger'
|
||||||
|
label='Delete'
|
||||||
|
onClick={() => {
|
||||||
|
deleteFilter(filterItem.id)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<div className="flex">
|
||||||
|
<BaseButton
|
||||||
|
className="my-2 mr-3"
|
||||||
|
color="success"
|
||||||
|
label='Apply'
|
||||||
|
onClick={handleSubmit}
|
||||||
|
/>
|
||||||
|
<BaseButton
|
||||||
|
className="my-2"
|
||||||
|
color='info'
|
||||||
|
label='Cancel'
|
||||||
|
onClick={handleReset}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox> : null
|
||||||
|
}
|
||||||
|
<CardBoxModal
|
||||||
|
title="Please confirm"
|
||||||
|
buttonColor="info"
|
||||||
|
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
||||||
|
isActive={isModalTrashActive}
|
||||||
|
onConfirm={handleDeleteAction}
|
||||||
|
onCancel={handleModalAction}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete this item?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
|
||||||
|
|
||||||
|
{inventory_movements && Array.isArray(inventory_movements) && !showGrid && (
|
||||||
|
<ListInventory_movements
|
||||||
|
inventory_movements={inventory_movements}
|
||||||
|
loading={loading}
|
||||||
|
onDelete={handleDeleteModalAction}
|
||||||
|
currentPage={currentPage}
|
||||||
|
numPages={numPages}
|
||||||
|
onPageChange={onPageChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{showGrid && dataGrid}
|
||||||
|
|
||||||
|
|
||||||
|
{selectedRows.length > 0 &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
className='me-4'
|
||||||
|
color='danger'
|
||||||
|
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
||||||
|
onClick={() => onDeleteRows(selectedRows)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button'),
|
||||||
|
)}
|
||||||
|
<ToastContainer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TableSampleInventory_movements
|
||||||
@ -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 [
|
||||||
|
<div key={params?.row?.id}>
|
||||||
|
<ListActionsPopover
|
||||||
|
onDelete={onDelete}
|
||||||
|
itemId={params?.row?.id}
|
||||||
|
pathEdit={`/inventory_movements/inventory_movements-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/inventory_movements/inventory_movements-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
@ -34,7 +34,7 @@ const KanbanCard = ({
|
|||||||
<div
|
<div
|
||||||
ref={drag}
|
ref={drag}
|
||||||
className={
|
className={
|
||||||
`bg-skyBlueTheme-cardColor dark:bg-dark-800 rounded-md space-y-2 p-4 relative ${isDragging ? 'cursor-grabbing' : 'cursor-grab'}`
|
`bg-gray-50 dark:bg-dark-800 rounded-md space-y-2 p-4 relative ${isDragging ? 'cursor-grabbing' : 'cursor-grab'}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className={'flex items-center justify-between'}>
|
<div className={'flex items-center justify-between'}>
|
||||||
|
|||||||
@ -188,7 +188,7 @@ const KanbanColumn = ({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!data?.length && (
|
{!data?.length && (
|
||||||
<p className={'text-center py-8 bg-skyBlueTheme-cardColor dark:bg-dark-800'}>No data</p>
|
<p className={'text-center py-8 bg-gray-50 dark:bg-dark-800'}>No data</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
|||||||
@ -53,7 +53,7 @@ const ListActionsPopover = ({
|
|||||||
size={'small'}
|
size={'small'}
|
||||||
>
|
>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
className={`text-primaryText dark:text-white ${iconClassName}`}
|
className={`text-black dark:text-white ${iconClassName}`}
|
||||||
w='w-10'
|
w='w-10'
|
||||||
h='h-10'
|
h='h-10'
|
||||||
size={24}
|
size={24}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const LoadingSpinner = () => {
|
|||||||
className='w-12 h-12 rounded-full absolute border-4 border-solid border-gray-200 dark:border-slate-800'
|
className='w-12 h-12 rounded-full absolute border-4 border-solid border-gray-200 dark:border-slate-800'
|
||||||
></div>
|
></div>
|
||||||
<div
|
<div
|
||||||
className="w-12 h-12 rounded-full animate-spin absolute border-4 border-solid border-skyBlueTheme-iconsColor dark:border-blue-500 border-t-transparent"
|
className="w-12 h-12 rounded-full animate-spin absolute border-4 border-solid border-blue-500 dark:border-blue-500 border-t-transparent"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
<nav
|
<nav
|
||||||
className={`${className} top-0 inset-x-0 fixed ${bgColor} h-14 z-30 transition-position w-screen lg:w-auto dark:bg-dark-800`}
|
className={`${className} top-0 inset-x-0 fixed ${bgColor} h-14 z-30 transition-position w-screen lg:w-auto dark:bg-dark-800`}
|
||||||
>
|
>
|
||||||
<div className={`flex lg:items-stretch ${containerMaxW} ${isScrolled && `border-b border-skyBlueTheme-outsideCardColor dark:border-dark-700`}`}>
|
<div className={`flex lg:items-stretch ${containerMaxW} ${isScrolled && `border-b border-pavitra-400 dark:border-dark-700`}`}>
|
||||||
<div className="flex flex-1 items-stretch h-14">{children}</div>
|
<div className="flex flex-1 items-stretch h-14">{children}</div>
|
||||||
<div className="flex-none items-stretch flex h-14 lg:hidden">
|
<div className="flex-none items-stretch flex h-14 lg:hidden">
|
||||||
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export default function NavBarItem({ item }: Props) {
|
|||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
!isDropdownActive ? 'lg:hidden' : ''
|
!isDropdownActive ? 'lg:hidden' : ''
|
||||||
} text-sm border-b border-gray-100 lg:border lg:bg-skyBlueTheme-cardColor lg:absolute lg:top-full lg:left-0 lg:min-w-full lg:z-20 lg:rounded-lg lg:shadow-lg lg:dark:bg-dark-900 dark:border-dark-700`}
|
} text-sm border-b border-gray-100 lg:border lg:bg-white lg:absolute lg:top-full lg:left-0 lg:min-w-full lg:z-20 lg:rounded-lg lg:shadow-lg lg:dark:bg-dark-900 dark:border-dark-700`}
|
||||||
>
|
>
|
||||||
<ClickOutside onClickOutside={() => setIsDropdownActive(false)} excludedElements={[excludedRef]}>
|
<ClickOutside onClickOutside={() => setIsDropdownActive(false)} excludedElements={[excludedRef]}>
|
||||||
<NavBarMenuList menu={item.menu} />
|
<NavBarMenuList menu={item.menu} />
|
||||||
|
|||||||
@ -58,7 +58,7 @@ const CardOrder_items = ({
|
|||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
<Link href={`/order_items/order_items-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
<Link href={`/order_items/order_items-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
{item.name}
|
{item.product_name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
@ -76,6 +76,78 @@ const CardOrder_items = ({
|
|||||||
</div>
|
</div>
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>ProductName</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.product_name }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Product</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.productsOneListFormatter(item.product) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Quantity</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.quantity }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>UnitPrice</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.unit_price }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Total</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.total }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>SKU</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.sku }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const ListOrder_items = ({ order_items, loading, onDelete, currentPage, numPages
|
|||||||
{!loading && order_items.map((item) => (
|
{!loading && order_items.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex ${bgColor} ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/order_items/order_items-view/?id=${item.id}`}
|
href={`/order_items/order_items-view/?id=${item.id}`}
|
||||||
@ -46,6 +46,54 @@ const ListOrder_items = ({ order_items, loading, onDelete, currentPage, numPages
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>ProductName</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.product_name }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Product</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.productsOneListFormatter(item.product) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Quantity</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.quantity }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>UnitPrice</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.unit_price }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Total</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.total }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>SKU</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.sku }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
|
|||||||
@ -412,13 +412,13 @@ const TableSampleOrder_items = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2 mr-3"
|
className="my-2 mr-3"
|
||||||
type='submit' color='info'
|
color="success"
|
||||||
label='Apply'
|
label='Apply'
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2"
|
className="my-2"
|
||||||
type='reset' color='info' outline
|
color='info'
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -41,6 +41,106 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'product_name',
|
||||||
|
headerName: 'ProductName',
|
||||||
|
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: 'quantity',
|
||||||
|
headerName: 'Quantity',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'number',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'unit_price',
|
||||||
|
headerName: 'UnitPrice',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'number',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'total',
|
||||||
|
headerName: 'Total',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'number',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'sku',
|
||||||
|
headerName: 'SKU',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
|
|||||||
@ -76,6 +76,186 @@ const CardOrders = ({
|
|||||||
</div>
|
</div>
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>OrderNumber</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.order_number }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Customer</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.customersOneListFormatter(item.customer) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Status</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.status }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>OrderDate</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.dateTimeFormatter(item.order_date) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>DeliveryDate</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.dateTimeFormatter(item.delivery_date) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Subtotal</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.subtotal }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Tax</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.tax }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>ShippingFee</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.shipping_fee }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Total</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.total }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>ShippingAddress</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.shipping_address }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>BillingAddress</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.billing_address }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>OrderItems</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.order_itemsManyListFormatter(item.items).join(', ')}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Payments</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.paymentsManyListFormatter(item.payments).join(', ')}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Shipments</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.shipmentsManyListFormatter(item.shipments).join(', ')}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>AssignedTo</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.usersOneListFormatter(item.assigned_to) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user