v1
This commit is contained in:
parent
e434ee621a
commit
3e9432e89e
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
/backend/node_modules
|
||||
/frontend/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
|
||||
available.</p>
|
||||
<div class="project-info">
|
||||
<h2>StoreOps Manager</h2>
|
||||
<p>Manage products, inventory, orders, customers, and payments with Admin and Staff roles.</p>
|
||||
<h2>Store Operations Manager</h2>
|
||||
<p>Store Operations Manager for catalog and full order lifecycle with products, customers, payments, and reporting.</p>
|
||||
</div>
|
||||
<div class="loader-container">
|
||||
<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).
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
DB_NAME=app_37379
|
||||
DB_USER=app_37379
|
||||
DB_PASS=fc0de3ba-4aee-4507-88c3-64c04c521f22
|
||||
DB_NAME=app_37384
|
||||
DB_USER=app_37384
|
||||
DB_PASS=4df3d09c-78bc-4451-8952-4e97b44f02af
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=5432
|
||||
PORT=3000
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
#StoreOps Manager - template backend,
|
||||
#Store Operations Manager - template backend,
|
||||
|
||||
#### Run App on local machine:
|
||||
|
||||
@ -30,10 +30,10 @@
|
||||
- `psql postgres -U admin`
|
||||
|
||||
- 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`.
|
||||
- `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`
|
||||
|
||||
------------
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "storeopsmanager",
|
||||
"description": "StoreOps Manager - template backend",
|
||||
"name": "storeoperationsmanager",
|
||||
"description": "Store Operations Manager - template backend",
|
||||
"scripts": {
|
||||
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
||||
"db:migrate": "sequelize-cli db:migrate",
|
||||
|
||||
@ -11,15 +11,15 @@ const config = {
|
||||
bcrypt: {
|
||||
saltRounds: 12
|
||||
},
|
||||
admin_pass: "fc0de3ba",
|
||||
user_pass: "64c04c521f22",
|
||||
admin_pass: "4df3d09c",
|
||||
user_pass: "4e97b44f02af",
|
||||
admin_email: "admin@flatlogic.com",
|
||||
providers: {
|
||||
LOCAL: 'local',
|
||||
GOOGLE: 'google',
|
||||
MICROSOFT: 'microsoft'
|
||||
},
|
||||
secret_key: process.env.SECRET_KEY || 'fc0de3ba-4aee-4507-88c3-64c04c521f22',
|
||||
secret_key: process.env.SECRET_KEY || '4df3d09c-78bc-4451-8952-4e97b44f02af',
|
||||
remote: '',
|
||||
port: process.env.NODE_ENV === "production" ? "" : "8080",
|
||||
hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost",
|
||||
@ -39,7 +39,7 @@ const config = {
|
||||
},
|
||||
uploadDir: os.tmpdir(),
|
||||
email: {
|
||||
from: 'StoreOps Manager <app@flatlogic.app>',
|
||||
from: 'Store Operations Manager <app@flatlogic.app>',
|
||||
host: 'email-smtp.us-east-1.amazonaws.com',
|
||||
port: 587,
|
||||
auth: {
|
||||
@ -60,7 +60,7 @@ const config = {
|
||||
|
||||
},
|
||||
|
||||
project_uuid: 'fc0de3ba-4aee-4507-88c3-64c04c521f22',
|
||||
project_uuid: '4df3d09c-78bc-4451-8952-4e97b44f02af',
|
||||
flHost: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@ const config = {
|
||||
|
||||
config.pexelsKey = process.env.PEXELS_KEY || '';
|
||||
|
||||
config.pexelsQuery = 'sunrise over calm ocean with birds';
|
||||
config.pexelsQuery = 'compass on vintage map';
|
||||
config.host = process.env.NODE_ENV === "production" ? config.remote : "http://localhost";
|
||||
config.apiUrl = `${config.host}${config.port ? `:${config.port}` : ``}/api`;
|
||||
config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`;
|
||||
|
||||
@ -195,6 +195,8 @@ module.exports = class CategoriesDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
output.products_category = await categories.getProducts_category({
|
||||
transaction
|
||||
});
|
||||
@ -208,6 +210,7 @@ module.exports = class CategoriesDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
output.parent = await categories.getParent({
|
||||
transaction
|
||||
});
|
||||
|
||||
@ -36,12 +36,12 @@ module.exports = class CustomersDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
billing_address: data.billing_address
|
||||
company: data.company
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
shipping_address: data.shipping_address
|
||||
address: data.address
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -51,10 +51,9 @@ module.exports = class CustomersDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
vip: data.vip
|
||||
loyalty_points: data.loyalty_points
|
||||
||
|
||||
false
|
||||
|
||||
null
|
||||
,
|
||||
|
||||
importHash: data.importHash || null,
|
||||
@ -97,12 +96,12 @@ module.exports = class CustomersDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
billing_address: item.billing_address
|
||||
company: item.company
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
shipping_address: item.shipping_address
|
||||
address: item.address
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -112,10 +111,9 @@ module.exports = class CustomersDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
vip: item.vip
|
||||
loyalty_points: item.loyalty_points
|
||||
||
|
||||
false
|
||||
|
||||
null
|
||||
,
|
||||
|
||||
importHash: item.importHash || null,
|
||||
@ -154,16 +152,16 @@ module.exports = class CustomersDBApi {
|
||||
if (data.phone !== undefined) updatePayload.phone = data.phone;
|
||||
|
||||
|
||||
if (data.billing_address !== undefined) updatePayload.billing_address = data.billing_address;
|
||||
if (data.company !== undefined) updatePayload.company = data.company;
|
||||
|
||||
|
||||
if (data.shipping_address !== undefined) updatePayload.shipping_address = data.shipping_address;
|
||||
if (data.address !== undefined) updatePayload.address = data.address;
|
||||
|
||||
|
||||
if (data.notes !== undefined) updatePayload.notes = data.notes;
|
||||
|
||||
|
||||
if (data.vip !== undefined) updatePayload.vip = data.vip;
|
||||
if (data.loyalty_points !== undefined) updatePayload.loyalty_points = data.loyalty_points;
|
||||
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
@ -252,6 +250,9 @@ module.exports = class CustomersDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
output.orders_customer = await customers.getOrders_customer({
|
||||
transaction
|
||||
});
|
||||
@ -333,24 +334,24 @@ module.exports = class CustomersDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.billing_address) {
|
||||
if (filter.company) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'customers',
|
||||
'billing_address',
|
||||
filter.billing_address,
|
||||
'company',
|
||||
filter.company,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.shipping_address) {
|
||||
if (filter.address) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'customers',
|
||||
'shipping_address',
|
||||
filter.shipping_address,
|
||||
'address',
|
||||
filter.address,
|
||||
),
|
||||
};
|
||||
}
|
||||
@ -371,6 +372,30 @@ module.exports = class CustomersDBApi {
|
||||
|
||||
|
||||
|
||||
if (filter.loyalty_pointsRange) {
|
||||
const [start, end] = filter.loyalty_pointsRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
loyalty_points: {
|
||||
...where.loyalty_points,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
loyalty_points: {
|
||||
...where.loyalty_points,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
@ -380,13 +405,6 @@ module.exports = class CustomersDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.vip) {
|
||||
where = {
|
||||
...where,
|
||||
vip: filter.vip,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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 Op = Sequelize.Op;
|
||||
|
||||
module.exports = class InventoryDBApi {
|
||||
module.exports = class Inventory_movementsDBApi {
|
||||
|
||||
|
||||
|
||||
@ -17,27 +17,27 @@ module.exports = class InventoryDBApi {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const inventory = await db.inventory.create(
|
||||
const inventory_movements = await db.inventory_movements.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
quantity: data.quantity
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
location: data.location
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
change_type: data.change_type
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
note: data.note
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
change: data.change
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
reason: data.reason
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
date: data.date
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
@ -49,7 +49,7 @@ module.exports = class InventoryDBApi {
|
||||
);
|
||||
|
||||
|
||||
await inventory.setVariant( data.variant || null, {
|
||||
await inventory_movements.setProduct( data.product || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -58,7 +58,7 @@ module.exports = class InventoryDBApi {
|
||||
|
||||
|
||||
|
||||
return inventory;
|
||||
return inventory_movements;
|
||||
}
|
||||
|
||||
|
||||
@ -67,27 +67,27 @@ module.exports = class InventoryDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const inventoryData = data.map((item, index) => ({
|
||||
const inventory_movementsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
quantity: item.quantity
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
location: item.location
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
change_type: item.change_type
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
note: item.note
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
change: item.change
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
reason: item.reason
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
date: item.date
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
importHash: item.importHash || null,
|
||||
@ -97,12 +97,12 @@ module.exports = class InventoryDBApi {
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const inventory = await db.inventory.bulkCreate(inventoryData, { transaction });
|
||||
const inventory_movements = await db.inventory_movements.bulkCreate(inventory_movementsData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
|
||||
return inventory;
|
||||
return inventory_movements;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
@ -110,35 +110,35 @@ module.exports = class InventoryDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
|
||||
const inventory = await db.inventory.findByPk(id, {}, {transaction});
|
||||
const inventory_movements = await db.inventory_movements.findByPk(id, {}, {transaction});
|
||||
|
||||
|
||||
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.quantity !== undefined) updatePayload.quantity = data.quantity;
|
||||
|
||||
|
||||
if (data.location !== undefined) updatePayload.location = data.location;
|
||||
|
||||
|
||||
if (data.change_type !== undefined) updatePayload.change_type = data.change_type;
|
||||
|
||||
|
||||
if (data.note !== undefined) updatePayload.note = data.note;
|
||||
|
||||
|
||||
if (data.change !== undefined) updatePayload.change = data.change;
|
||||
|
||||
|
||||
if (data.reason !== undefined) updatePayload.reason = data.reason;
|
||||
|
||||
|
||||
if (data.date !== undefined) updatePayload.date = data.date;
|
||||
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await inventory.update(updatePayload, {transaction});
|
||||
await inventory_movements.update(updatePayload, {transaction});
|
||||
|
||||
|
||||
|
||||
if (data.variant !== undefined) {
|
||||
await inventory.setVariant(
|
||||
if (data.product !== undefined) {
|
||||
await inventory_movements.setProduct(
|
||||
|
||||
data.variant,
|
||||
data.product,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -150,14 +150,14 @@ module.exports = class InventoryDBApi {
|
||||
|
||||
|
||||
|
||||
return inventory;
|
||||
return inventory_movements;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const inventory = await db.inventory.findAll({
|
||||
const inventory_movements = await db.inventory_movements.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
@ -167,53 +167,53 @@ module.exports = class InventoryDBApi {
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of inventory) {
|
||||
for (const record of inventory_movements) {
|
||||
await record.update(
|
||||
{deletedBy: currentUser.id},
|
||||
{transaction}
|
||||
);
|
||||
}
|
||||
for (const record of inventory) {
|
||||
for (const record of inventory_movements) {
|
||||
await record.destroy({transaction});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return inventory;
|
||||
return inventory_movements;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const inventory = await db.inventory.findByPk(id, options);
|
||||
const inventory_movements = await db.inventory_movements.findByPk(id, options);
|
||||
|
||||
await inventory.update({
|
||||
await inventory_movements.update({
|
||||
deletedBy: currentUser.id
|
||||
}, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await inventory.destroy({
|
||||
await inventory_movements.destroy({
|
||||
transaction
|
||||
});
|
||||
|
||||
return inventory;
|
||||
return inventory_movements;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const inventory = await db.inventory.findOne(
|
||||
const inventory_movements = await db.inventory_movements.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!inventory) {
|
||||
return inventory;
|
||||
if (!inventory_movements) {
|
||||
return inventory_movements;
|
||||
}
|
||||
|
||||
const output = inventory.get({plain: true});
|
||||
const output = inventory_movements.get({plain: true});
|
||||
|
||||
|
||||
|
||||
@ -230,7 +230,10 @@ module.exports = class InventoryDBApi {
|
||||
|
||||
|
||||
|
||||
output.variant = await inventory.getVariant({
|
||||
|
||||
|
||||
|
||||
output.product = await inventory_movements.getProduct({
|
||||
transaction
|
||||
});
|
||||
|
||||
@ -261,15 +264,15 @@ module.exports = class InventoryDBApi {
|
||||
let include = [
|
||||
|
||||
{
|
||||
model: db.product_variants,
|
||||
as: 'variant',
|
||||
model: db.products,
|
||||
as: 'product',
|
||||
|
||||
where: filter.variant ? {
|
||||
where: filter.product ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: filter.variant.split('|').map(term => Utils.uuid(term)) } },
|
||||
{ id: { [Op.in]: filter.product.split('|').map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
sku: {
|
||||
[Op.or]: filter.variant.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
name: {
|
||||
[Op.or]: filter.product.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
},
|
||||
]
|
||||
@ -290,22 +293,11 @@ module.exports = class InventoryDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.location) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'inventory',
|
||||
'location',
|
||||
filter.location,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.note) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'inventory',
|
||||
'inventory_movements',
|
||||
'note',
|
||||
filter.note,
|
||||
),
|
||||
@ -317,14 +309,14 @@ module.exports = class InventoryDBApi {
|
||||
|
||||
|
||||
|
||||
if (filter.quantityRange) {
|
||||
const [start, end] = filter.quantityRange;
|
||||
if (filter.changeRange) {
|
||||
const [start, end] = filter.changeRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
quantity: {
|
||||
...where.quantity,
|
||||
change: {
|
||||
...where.change,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
@ -333,8 +325,32 @@ module.exports = class InventoryDBApi {
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
quantity: {
|
||||
...where.quantity,
|
||||
change: {
|
||||
...where.change,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.dateRange) {
|
||||
const [start, end] = filter.dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
date: {
|
||||
...where.date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
date: {
|
||||
...where.date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
@ -350,10 +366,10 @@ module.exports = class InventoryDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.change_type) {
|
||||
if (filter.reason) {
|
||||
where = {
|
||||
...where,
|
||||
change_type: filter.change_type,
|
||||
reason: filter.reason,
|
||||
};
|
||||
}
|
||||
|
||||
@ -408,7 +424,7 @@ module.exports = class InventoryDBApi {
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.inventory.findAndCountAll(queryOptions);
|
||||
const { rows, count } = await db.inventory_movements.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
@ -430,25 +446,25 @@ module.exports = class InventoryDBApi {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike(
|
||||
'inventory',
|
||||
'location',
|
||||
'inventory_movements',
|
||||
'note',
|
||||
query,
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.inventory.findAll({
|
||||
attributes: [ 'id', 'location' ],
|
||||
const records = await db.inventory_movements.findAll({
|
||||
attributes: [ 'id', 'note' ],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['location', 'ASC']],
|
||||
orderBy: [['note', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.location,
|
||||
label: record.note,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ module.exports = class Order_itemsDBApi {
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name
|
||||
product_name: data.product_name
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -38,6 +38,11 @@ module.exports = class Order_itemsDBApi {
|
||||
|
||||
total: data.total
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
sku: data.sku
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
@ -49,11 +54,7 @@ module.exports = class Order_itemsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await order_items.setOrder( data.order || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await order_items.setProduct_variant( data.product_variant || null, {
|
||||
await order_items.setProduct( data.product || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -74,7 +75,7 @@ module.exports = class Order_itemsDBApi {
|
||||
const order_itemsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
name: item.name
|
||||
product_name: item.product_name
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -92,6 +93,11 @@ module.exports = class Order_itemsDBApi {
|
||||
total: item.total
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
sku: item.sku
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
importHash: item.importHash || null,
|
||||
@ -121,7 +127,7 @@ module.exports = class Order_itemsDBApi {
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
if (data.product_name !== undefined) updatePayload.product_name = data.product_name;
|
||||
|
||||
|
||||
if (data.quantity !== undefined) updatePayload.quantity = data.quantity;
|
||||
@ -133,25 +139,19 @@ module.exports = class Order_itemsDBApi {
|
||||
if (data.total !== undefined) updatePayload.total = data.total;
|
||||
|
||||
|
||||
if (data.sku !== undefined) updatePayload.sku = data.sku;
|
||||
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await order_items.update(updatePayload, {transaction});
|
||||
|
||||
|
||||
|
||||
if (data.order !== undefined) {
|
||||
await order_items.setOrder(
|
||||
if (data.product !== undefined) {
|
||||
await order_items.setProduct(
|
||||
|
||||
data.order,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.product_variant !== undefined) {
|
||||
await order_items.setProduct_variant(
|
||||
|
||||
data.product_variant,
|
||||
data.product,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -243,12 +243,10 @@ module.exports = class Order_itemsDBApi {
|
||||
|
||||
|
||||
|
||||
output.order = await order_items.getOrder({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.product_variant = await order_items.getProduct_variant({
|
||||
|
||||
output.product = await order_items.getProduct({
|
||||
transaction
|
||||
});
|
||||
|
||||
@ -279,32 +277,15 @@ module.exports = class Order_itemsDBApi {
|
||||
let include = [
|
||||
|
||||
{
|
||||
model: db.orders,
|
||||
as: 'order',
|
||||
model: db.products,
|
||||
as: 'product',
|
||||
|
||||
where: filter.order ? {
|
||||
where: filter.product ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } },
|
||||
{ id: { [Op.in]: filter.product.split('|').map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
order_number: {
|
||||
[Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
},
|
||||
]
|
||||
} : {},
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
model: db.product_variants,
|
||||
as: 'product_variant',
|
||||
|
||||
where: filter.product_variant ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: filter.product_variant.split('|').map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
sku: {
|
||||
[Op.or]: filter.product_variant.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
name: {
|
||||
[Op.or]: filter.product.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
},
|
||||
]
|
||||
@ -325,13 +306,24 @@ module.exports = class Order_itemsDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.name) {
|
||||
if (filter.product_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'order_items',
|
||||
'name',
|
||||
filter.name,
|
||||
'product_name',
|
||||
filter.product_name,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.sku) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'order_items',
|
||||
'sku',
|
||||
filter.sku,
|
||||
),
|
||||
};
|
||||
}
|
||||
@ -426,8 +418,6 @@ module.exports = class Order_itemsDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
@ -498,7 +488,7 @@ module.exports = class Order_itemsDBApi {
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike(
|
||||
'order_items',
|
||||
'name',
|
||||
'product_name',
|
||||
query,
|
||||
),
|
||||
],
|
||||
@ -506,16 +496,16 @@ module.exports = class Order_itemsDBApi {
|
||||
}
|
||||
|
||||
const records = await db.order_items.findAll({
|
||||
attributes: [ 'id', 'name' ],
|
||||
attributes: [ 'id', 'product_name' ],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['name', 'ASC']],
|
||||
orderBy: [['product_name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.name,
|
||||
label: record.product_name,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@ -31,28 +31,43 @@ module.exports = class OrdersDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
order_date: data.order_date
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
delivery_date: data.delivery_date
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
subtotal: data.subtotal
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
tax: data.tax
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
shipping_fee: data.shipping_fee
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
total: data.total
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
placed_at: data.placed_at
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
shipped_at: data.shipped_at
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
payment_status: data.payment_status
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
shipping_address: data.shipping_address
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
billing_address: data.billing_address
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
@ -68,8 +83,24 @@ module.exports = class OrdersDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await orders.setAssigned_to( data.assigned_to || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
|
||||
|
||||
await orders.setItems(data.items || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await orders.setPayments(data.payments || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await orders.setShipments(data.shipments || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@ -93,31 +124,46 @@ module.exports = class OrdersDBApi {
|
||||
status: item.status
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
order_date: item.order_date
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
delivery_date: item.delivery_date
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
subtotal: item.subtotal
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
tax: item.tax
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
shipping_fee: item.shipping_fee
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
total: item.total
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
placed_at: item.placed_at
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
shipped_at: item.shipped_at
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
payment_status: item.payment_status
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
shipping_address: item.shipping_address
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
billing_address: item.billing_address
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
importHash: item.importHash || null,
|
||||
@ -153,21 +199,30 @@ module.exports = class OrdersDBApi {
|
||||
if (data.status !== undefined) updatePayload.status = data.status;
|
||||
|
||||
|
||||
if (data.order_date !== undefined) updatePayload.order_date = data.order_date;
|
||||
|
||||
|
||||
if (data.delivery_date !== undefined) updatePayload.delivery_date = data.delivery_date;
|
||||
|
||||
|
||||
if (data.subtotal !== undefined) updatePayload.subtotal = data.subtotal;
|
||||
|
||||
|
||||
if (data.tax !== undefined) updatePayload.tax = data.tax;
|
||||
|
||||
|
||||
if (data.shipping_fee !== undefined) updatePayload.shipping_fee = data.shipping_fee;
|
||||
|
||||
|
||||
if (data.total !== undefined) updatePayload.total = data.total;
|
||||
|
||||
|
||||
if (data.placed_at !== undefined) updatePayload.placed_at = data.placed_at;
|
||||
|
||||
|
||||
if (data.shipped_at !== undefined) updatePayload.shipped_at = data.shipped_at;
|
||||
|
||||
|
||||
if (data.payment_status !== undefined) updatePayload.payment_status = data.payment_status;
|
||||
|
||||
|
||||
if (data.shipping_address !== undefined) updatePayload.shipping_address = data.shipping_address;
|
||||
|
||||
|
||||
if (data.billing_address !== undefined) updatePayload.billing_address = data.billing_address;
|
||||
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await orders.update(updatePayload, {transaction});
|
||||
@ -183,9 +238,30 @@ module.exports = class OrdersDBApi {
|
||||
);
|
||||
}
|
||||
|
||||
if (data.assigned_to !== undefined) {
|
||||
await orders.setAssigned_to(
|
||||
|
||||
data.assigned_to,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (data.items !== undefined) {
|
||||
await orders.setItems(data.items, { transaction });
|
||||
}
|
||||
|
||||
if (data.payments !== undefined) {
|
||||
await orders.setPayments(data.payments, { transaction });
|
||||
}
|
||||
|
||||
if (data.shipments !== undefined) {
|
||||
await orders.setShipments(data.shipments, { transaction });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -264,28 +340,43 @@ module.exports = class OrdersDBApi {
|
||||
|
||||
|
||||
|
||||
output.order_items_order = await orders.getOrder_items_order({
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
output.returns_order = await orders.getReturns_order({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.payments_order = await orders.getPayments_order({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.shipments_order = await orders.getShipments_order({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
output.customer = await orders.getCustomer({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.items = await orders.getItems({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.payments = await orders.getPayments({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.shipments = await orders.getShipments({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.assigned_to = await orders.getAssigned_to({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
|
||||
return output;
|
||||
}
|
||||
@ -328,6 +419,41 @@ module.exports = class OrdersDBApi {
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
model: db.users,
|
||||
as: 'assigned_to',
|
||||
|
||||
where: filter.assigned_to ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: filter.assigned_to.split('|').map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
firstName: {
|
||||
[Op.or]: filter.assigned_to.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
},
|
||||
]
|
||||
} : {},
|
||||
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
model: db.order_items,
|
||||
as: 'items',
|
||||
required: false,
|
||||
},
|
||||
|
||||
{
|
||||
model: db.payments,
|
||||
as: 'payments',
|
||||
required: false,
|
||||
},
|
||||
|
||||
{
|
||||
model: db.shipments,
|
||||
as: 'shipments',
|
||||
required: false,
|
||||
},
|
||||
|
||||
|
||||
];
|
||||
@ -363,11 +489,160 @@ module.exports = class OrdersDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.billing_address) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'orders',
|
||||
'billing_address',
|
||||
filter.billing_address,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (filter.calendarStart && filter.calendarEnd) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.or]: [
|
||||
{
|
||||
order_date: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
{
|
||||
delivery_date: {
|
||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (filter.order_dateRange) {
|
||||
const [start, end] = filter.order_dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
order_date: {
|
||||
...where.order_date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
order_date: {
|
||||
...where.order_date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.delivery_dateRange) {
|
||||
const [start, end] = filter.delivery_dateRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
delivery_date: {
|
||||
...where.delivery_date,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
delivery_date: {
|
||||
...where.delivery_date,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.subtotalRange) {
|
||||
const [start, end] = filter.subtotalRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
subtotal: {
|
||||
...where.subtotal,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
subtotal: {
|
||||
...where.subtotal,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.taxRange) {
|
||||
const [start, end] = filter.taxRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
tax: {
|
||||
...where.tax,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
tax: {
|
||||
...where.tax,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.shipping_feeRange) {
|
||||
const [start, end] = filter.shipping_feeRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
shipping_fee: {
|
||||
...where.shipping_fee,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
shipping_fee: {
|
||||
...where.shipping_fee,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.totalRange) {
|
||||
const [start, end] = filter.totalRange;
|
||||
|
||||
@ -392,54 +667,6 @@ module.exports = class OrdersDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.placed_atRange) {
|
||||
const [start, end] = filter.placed_atRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
placed_at: {
|
||||
...where.placed_at,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
placed_at: {
|
||||
...where.placed_at,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.shipped_atRange) {
|
||||
const [start, end] = filter.shipped_atRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
shipped_at: {
|
||||
...where.shipped_at,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
shipped_at: {
|
||||
...where.shipped_at,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
@ -456,17 +683,81 @@ module.exports = class OrdersDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.payment_status) {
|
||||
where = {
|
||||
...where,
|
||||
payment_status: filter.payment_status,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (filter.items) {
|
||||
const searchTerms = filter.items.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.order_items,
|
||||
as: 'items_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where: searchTerms.length > 0 ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
product_name: {
|
||||
[Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
}
|
||||
]
|
||||
} : undefined
|
||||
},
|
||||
...include,
|
||||
]
|
||||
}
|
||||
|
||||
if (filter.payments) {
|
||||
const searchTerms = filter.payments.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.payments,
|
||||
as: 'payments_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where: searchTerms.length > 0 ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
transaction_ref: {
|
||||
[Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
}
|
||||
]
|
||||
} : undefined
|
||||
},
|
||||
...include,
|
||||
]
|
||||
}
|
||||
|
||||
if (filter.shipments) {
|
||||
const searchTerms = filter.shipments.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.shipments,
|
||||
as: 'shipments_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where: searchTerms.length > 0 ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
tracking_number: {
|
||||
[Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
}
|
||||
]
|
||||
} : undefined
|
||||
},
|
||||
...include,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
|
||||
@ -21,12 +21,7 @@ module.exports = class PaymentsDBApi {
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
transaction_id: data.transaction_id
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
payment_method: data.payment_method
|
||||
transaction_ref: data.transaction_ref
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -36,6 +31,11 @@ module.exports = class PaymentsDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
method: data.method
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
status: data.status
|
||||
||
|
||||
null
|
||||
@ -46,6 +46,17 @@ module.exports = class PaymentsDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
provider: data.provider
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
refunded: data.refunded
|
||||
||
|
||||
false
|
||||
|
||||
,
|
||||
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
@ -54,14 +65,20 @@ module.exports = class PaymentsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await payments.setOrder( data.order || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.payments.getTableName(),
|
||||
belongsToColumn: 'receipt',
|
||||
belongsToId: payments.id,
|
||||
},
|
||||
data.receipt,
|
||||
options,
|
||||
);
|
||||
|
||||
|
||||
return payments;
|
||||
}
|
||||
@ -75,12 +92,7 @@ module.exports = class PaymentsDBApi {
|
||||
const paymentsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
transaction_id: item.transaction_id
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
payment_method: item.payment_method
|
||||
transaction_ref: item.transaction_ref
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -88,6 +100,11 @@ module.exports = class PaymentsDBApi {
|
||||
amount: item.amount
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
method: item.method
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
status: item.status
|
||||
@ -98,6 +115,17 @@ module.exports = class PaymentsDBApi {
|
||||
paid_at: item.paid_at
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
provider: item.provider
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
refunded: item.refunded
|
||||
||
|
||||
false
|
||||
|
||||
,
|
||||
|
||||
importHash: item.importHash || null,
|
||||
@ -111,6 +139,18 @@ module.exports = class PaymentsDBApi {
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
for (let i = 0; i < payments.length; i++) {
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.payments.getTableName(),
|
||||
belongsToColumn: 'receipt',
|
||||
belongsToId: payments[i].id,
|
||||
},
|
||||
data[i].receipt,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return payments;
|
||||
}
|
||||
@ -127,41 +167,48 @@ module.exports = class PaymentsDBApi {
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.transaction_id !== undefined) updatePayload.transaction_id = data.transaction_id;
|
||||
|
||||
|
||||
if (data.payment_method !== undefined) updatePayload.payment_method = data.payment_method;
|
||||
if (data.transaction_ref !== undefined) updatePayload.transaction_ref = data.transaction_ref;
|
||||
|
||||
|
||||
if (data.amount !== undefined) updatePayload.amount = data.amount;
|
||||
|
||||
|
||||
if (data.method !== undefined) updatePayload.method = data.method;
|
||||
|
||||
|
||||
if (data.status !== undefined) updatePayload.status = data.status;
|
||||
|
||||
|
||||
if (data.paid_at !== undefined) updatePayload.paid_at = data.paid_at;
|
||||
|
||||
|
||||
if (data.provider !== undefined) updatePayload.provider = data.provider;
|
||||
|
||||
|
||||
if (data.refunded !== undefined) updatePayload.refunded = data.refunded;
|
||||
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await payments.update(updatePayload, {transaction});
|
||||
|
||||
|
||||
|
||||
if (data.order !== undefined) {
|
||||
await payments.setOrder(
|
||||
|
||||
data.order,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.payments.getTableName(),
|
||||
belongsToColumn: 'receipt',
|
||||
belongsToId: payments.id,
|
||||
},
|
||||
data.receipt,
|
||||
options,
|
||||
);
|
||||
|
||||
|
||||
return payments;
|
||||
}
|
||||
@ -243,7 +290,10 @@ module.exports = class PaymentsDBApi {
|
||||
|
||||
|
||||
|
||||
output.order = await payments.getOrder({
|
||||
|
||||
|
||||
|
||||
output.receipt = await payments.getReceipt({
|
||||
transaction
|
||||
});
|
||||
|
||||
@ -273,25 +323,13 @@ module.exports = class PaymentsDBApi {
|
||||
|
||||
let include = [
|
||||
|
||||
|
||||
|
||||
{
|
||||
model: db.orders,
|
||||
as: 'order',
|
||||
|
||||
where: filter.order ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
order_number: {
|
||||
[Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
},
|
||||
]
|
||||
} : {},
|
||||
|
||||
model: db.file,
|
||||
as: 'receipt',
|
||||
},
|
||||
|
||||
|
||||
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
@ -303,13 +341,24 @@ module.exports = class PaymentsDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.transaction_id) {
|
||||
if (filter.transaction_ref) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'payments',
|
||||
'transaction_id',
|
||||
filter.transaction_id,
|
||||
'transaction_ref',
|
||||
filter.transaction_ref,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.provider) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'payments',
|
||||
'provider',
|
||||
filter.provider,
|
||||
),
|
||||
};
|
||||
}
|
||||
@ -376,10 +425,10 @@ module.exports = class PaymentsDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.payment_method) {
|
||||
if (filter.method) {
|
||||
where = {
|
||||
...where,
|
||||
payment_method: filter.payment_method,
|
||||
method: filter.method,
|
||||
};
|
||||
}
|
||||
|
||||
@ -390,10 +439,15 @@ module.exports = class PaymentsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.refunded) {
|
||||
where = {
|
||||
...where,
|
||||
refunded: filter.refunded,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
@ -464,7 +518,7 @@ module.exports = class PaymentsDBApi {
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike(
|
||||
'payments',
|
||||
'transaction_id',
|
||||
'transaction_ref',
|
||||
query,
|
||||
),
|
||||
],
|
||||
@ -472,16 +526,16 @@ module.exports = class PaymentsDBApi {
|
||||
}
|
||||
|
||||
const records = await db.payments.findAll({
|
||||
attributes: [ 'id', 'transaction_id' ],
|
||||
attributes: [ 'id', 'transaction_ref' ],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['transaction_id', 'ASC']],
|
||||
orderBy: [['transaction_ref', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.transaction_id,
|
||||
label: record.transaction_ref,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@ -178,6 +178,9 @@ module.exports = class PermissionsDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@ -41,7 +41,17 @@ module.exports = class ProductsDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
weight: data.weight
|
||||
cost: data.cost
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
currency: data.currency
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
stock: data.stock
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -64,8 +74,16 @@ module.exports = class ProductsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await products.setSupplier( data.supplier || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
|
||||
|
||||
await products.setTags(data.tags || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
@ -111,7 +129,17 @@ module.exports = class ProductsDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
weight: item.weight
|
||||
cost: item.cost
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
currency: item.currency
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
stock: item.stock
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -173,7 +201,13 @@ module.exports = class ProductsDBApi {
|
||||
if (data.price !== undefined) updatePayload.price = data.price;
|
||||
|
||||
|
||||
if (data.weight !== undefined) updatePayload.weight = data.weight;
|
||||
if (data.cost !== undefined) updatePayload.cost = data.cost;
|
||||
|
||||
|
||||
if (data.currency !== undefined) updatePayload.currency = data.currency;
|
||||
|
||||
|
||||
if (data.stock !== undefined) updatePayload.stock = data.stock;
|
||||
|
||||
|
||||
if (data.active !== undefined) updatePayload.active = data.active;
|
||||
@ -194,9 +228,22 @@ module.exports = class ProductsDBApi {
|
||||
);
|
||||
}
|
||||
|
||||
if (data.supplier !== undefined) {
|
||||
await products.setSupplier(
|
||||
|
||||
data.supplier,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (data.tags !== undefined) {
|
||||
await products.setTags(data.tags, { transaction });
|
||||
}
|
||||
|
||||
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
@ -282,13 +329,24 @@ module.exports = class ProductsDBApi {
|
||||
|
||||
|
||||
|
||||
output.product_variants_product = await products.getProduct_variants_product({
|
||||
|
||||
|
||||
output.inventory_movements_product = await products.getInventory_movements_product({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.reviews_product = await products.getReviews_product({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
output.order_items_product = await products.getOrder_items_product({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@ -304,6 +362,16 @@ module.exports = class ProductsDBApi {
|
||||
});
|
||||
|
||||
|
||||
output.supplier = await products.getSupplier({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.tags = await products.getTags({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
|
||||
return output;
|
||||
}
|
||||
@ -346,6 +414,29 @@ module.exports = class ProductsDBApi {
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
model: db.suppliers,
|
||||
as: 'supplier',
|
||||
|
||||
where: filter.supplier ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: filter.supplier.split('|').map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.supplier.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
},
|
||||
]
|
||||
} : {},
|
||||
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
model: db.tags,
|
||||
as: 'tags',
|
||||
required: false,
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
@ -397,6 +488,17 @@ module.exports = class ProductsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.currency) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'products',
|
||||
'currency',
|
||||
filter.currency,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -426,14 +528,14 @@ module.exports = class ProductsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.weightRange) {
|
||||
const [start, end] = filter.weightRange;
|
||||
if (filter.costRange) {
|
||||
const [start, end] = filter.costRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
weight: {
|
||||
...where.weight,
|
||||
cost: {
|
||||
...where.cost,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
@ -442,8 +544,32 @@ module.exports = class ProductsDBApi {
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
weight: {
|
||||
...where.weight,
|
||||
cost: {
|
||||
...where.cost,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.stockRange) {
|
||||
const [start, end] = filter.stockRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
stock: {
|
||||
...where.stock,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
stock: {
|
||||
...where.stock,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
@ -470,6 +596,31 @@ module.exports = class ProductsDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (filter.tags) {
|
||||
const searchTerms = filter.tags.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.tags,
|
||||
as: 'tags_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where: searchTerms.length > 0 ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
name: {
|
||||
[Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
}
|
||||
]
|
||||
} : undefined
|
||||
},
|
||||
...include,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
|
||||
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 Op = Sequelize.Op;
|
||||
|
||||
module.exports = class Product_variantsDBApi {
|
||||
module.exports = class ReviewsDBApi {
|
||||
|
||||
|
||||
|
||||
@ -17,36 +17,31 @@ module.exports = class Product_variantsDBApi {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const product_variants = await db.product_variants.create(
|
||||
const reviews = await db.reviews.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
sku: data.sku
|
||||
author_name: data.author_name
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
title: data.title
|
||||
rating: data.rating
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
price: data.price
|
||||
comment: data.comment
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
stock: data.stock
|
||||
created: data.created
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
attributes: data.attributes
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
active: data.active
|
||||
approved: data.approved
|
||||
||
|
||||
false
|
||||
|
||||
@ -60,11 +55,7 @@ module.exports = class Product_variantsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await product_variants.setProduct( data.product || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await product_variants.setSupplier( data.supplier || null, {
|
||||
await reviews.setProduct( data.product || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -72,18 +63,8 @@ module.exports = class Product_variantsDBApi {
|
||||
|
||||
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.product_variants.getTableName(),
|
||||
belongsToColumn: 'images',
|
||||
belongsToId: product_variants.id,
|
||||
},
|
||||
data.images,
|
||||
options,
|
||||
);
|
||||
|
||||
|
||||
return product_variants;
|
||||
return reviews;
|
||||
}
|
||||
|
||||
|
||||
@ -92,35 +73,30 @@ module.exports = class Product_variantsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const product_variantsData = data.map((item, index) => ({
|
||||
const reviewsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
sku: item.sku
|
||||
author_name: item.author_name
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
title: item.title
|
||||
rating: item.rating
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
price: item.price
|
||||
comment: item.comment
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
stock: item.stock
|
||||
created: item.created
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
attributes: item.attributes
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
active: item.active
|
||||
approved: item.approved
|
||||
||
|
||||
false
|
||||
|
||||
@ -133,24 +109,12 @@ module.exports = class Product_variantsDBApi {
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const product_variants = await db.product_variants.bulkCreate(product_variantsData, { transaction });
|
||||
const reviews = await db.reviews.bulkCreate(reviewsData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
for (let i = 0; i < product_variants.length; i++) {
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.product_variants.getTableName(),
|
||||
belongsToColumn: 'images',
|
||||
belongsToId: product_variants[i].id,
|
||||
},
|
||||
data[i].images,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return product_variants;
|
||||
return reviews;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
@ -158,39 +122,36 @@ module.exports = class Product_variantsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
|
||||
const product_variants = await db.product_variants.findByPk(id, {}, {transaction});
|
||||
const reviews = await db.reviews.findByPk(id, {}, {transaction});
|
||||
|
||||
|
||||
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.sku !== undefined) updatePayload.sku = data.sku;
|
||||
if (data.author_name !== undefined) updatePayload.author_name = data.author_name;
|
||||
|
||||
|
||||
if (data.title !== undefined) updatePayload.title = data.title;
|
||||
if (data.rating !== undefined) updatePayload.rating = data.rating;
|
||||
|
||||
|
||||
if (data.price !== undefined) updatePayload.price = data.price;
|
||||
if (data.comment !== undefined) updatePayload.comment = data.comment;
|
||||
|
||||
|
||||
if (data.stock !== undefined) updatePayload.stock = data.stock;
|
||||
if (data.created !== undefined) updatePayload.created = data.created;
|
||||
|
||||
|
||||
if (data.attributes !== undefined) updatePayload.attributes = data.attributes;
|
||||
|
||||
|
||||
if (data.active !== undefined) updatePayload.active = data.active;
|
||||
if (data.approved !== undefined) updatePayload.approved = data.approved;
|
||||
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await product_variants.update(updatePayload, {transaction});
|
||||
await reviews.update(updatePayload, {transaction});
|
||||
|
||||
|
||||
|
||||
if (data.product !== undefined) {
|
||||
await product_variants.setProduct(
|
||||
await reviews.setProduct(
|
||||
|
||||
data.product,
|
||||
|
||||
@ -198,39 +159,20 @@ module.exports = class Product_variantsDBApi {
|
||||
);
|
||||
}
|
||||
|
||||
if (data.supplier !== undefined) {
|
||||
await product_variants.setSupplier(
|
||||
|
||||
data.supplier,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.product_variants.getTableName(),
|
||||
belongsToColumn: 'images',
|
||||
belongsToId: product_variants.id,
|
||||
},
|
||||
data.images,
|
||||
options,
|
||||
);
|
||||
|
||||
|
||||
return product_variants;
|
||||
return reviews;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const product_variants = await db.product_variants.findAll({
|
||||
const reviews = await db.reviews.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
@ -240,53 +182,53 @@ module.exports = class Product_variantsDBApi {
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of product_variants) {
|
||||
for (const record of reviews) {
|
||||
await record.update(
|
||||
{deletedBy: currentUser.id},
|
||||
{transaction}
|
||||
);
|
||||
}
|
||||
for (const record of product_variants) {
|
||||
for (const record of reviews) {
|
||||
await record.destroy({transaction});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return product_variants;
|
||||
return reviews;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const product_variants = await db.product_variants.findByPk(id, options);
|
||||
const reviews = await db.reviews.findByPk(id, options);
|
||||
|
||||
await product_variants.update({
|
||||
await reviews.update({
|
||||
deletedBy: currentUser.id
|
||||
}, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await product_variants.destroy({
|
||||
await reviews.destroy({
|
||||
transaction
|
||||
});
|
||||
|
||||
return product_variants;
|
||||
return reviews;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const product_variants = await db.product_variants.findOne(
|
||||
const reviews = await db.reviews.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!product_variants) {
|
||||
return product_variants;
|
||||
if (!reviews) {
|
||||
return reviews;
|
||||
}
|
||||
|
||||
const output = product_variants.get({plain: true});
|
||||
const output = reviews.get({plain: true});
|
||||
|
||||
|
||||
|
||||
@ -296,32 +238,17 @@ module.exports = class Product_variantsDBApi {
|
||||
|
||||
|
||||
|
||||
output.inventory_variant = await product_variants.getInventory_variant({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
|
||||
output.order_items_product_variant = await product_variants.getOrder_items_product_variant({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
output.product = await product_variants.getProduct({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.supplier = await product_variants.getSupplier({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
output.images = await product_variants.getImages({
|
||||
output.product = await reviews.getProduct({
|
||||
transaction
|
||||
});
|
||||
|
||||
@ -368,30 +295,8 @@ module.exports = class Product_variantsDBApi {
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
model: db.suppliers,
|
||||
as: 'supplier',
|
||||
|
||||
where: filter.supplier ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: filter.supplier.split('|').map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.supplier.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
},
|
||||
]
|
||||
} : {},
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
model: db.file,
|
||||
as: 'images',
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
@ -403,35 +308,24 @@ module.exports = class Product_variantsDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.sku) {
|
||||
if (filter.author_name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'product_variants',
|
||||
'sku',
|
||||
filter.sku,
|
||||
'reviews',
|
||||
'author_name',
|
||||
filter.author_name,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.title) {
|
||||
if (filter.comment) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'product_variants',
|
||||
'title',
|
||||
filter.title,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.attributes) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'product_variants',
|
||||
'attributes',
|
||||
filter.attributes,
|
||||
'reviews',
|
||||
'comment',
|
||||
filter.comment,
|
||||
),
|
||||
};
|
||||
}
|
||||
@ -441,14 +335,14 @@ module.exports = class Product_variantsDBApi {
|
||||
|
||||
|
||||
|
||||
if (filter.priceRange) {
|
||||
const [start, end] = filter.priceRange;
|
||||
if (filter.ratingRange) {
|
||||
const [start, end] = filter.ratingRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
price: {
|
||||
...where.price,
|
||||
rating: {
|
||||
...where.rating,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
@ -457,22 +351,22 @@ module.exports = class Product_variantsDBApi {
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
price: {
|
||||
...where.price,
|
||||
rating: {
|
||||
...where.rating,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.stockRange) {
|
||||
const [start, end] = filter.stockRange;
|
||||
if (filter.createdRange) {
|
||||
const [start, end] = filter.createdRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
stock: {
|
||||
...where.stock,
|
||||
created: {
|
||||
...where.created,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
@ -481,8 +375,8 @@ module.exports = class Product_variantsDBApi {
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
stock: {
|
||||
...where.stock,
|
||||
created: {
|
||||
...where.created,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
@ -498,10 +392,10 @@ module.exports = class Product_variantsDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.active) {
|
||||
if (filter.approved) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active,
|
||||
approved: filter.approved,
|
||||
};
|
||||
}
|
||||
|
||||
@ -509,8 +403,6 @@ module.exports = class Product_variantsDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
@ -558,7 +450,7 @@ module.exports = class Product_variantsDBApi {
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.product_variants.findAndCountAll(queryOptions);
|
||||
const { rows, count } = await db.reviews.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
@ -580,25 +472,25 @@ module.exports = class Product_variantsDBApi {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike(
|
||||
'product_variants',
|
||||
'sku',
|
||||
'reviews',
|
||||
'author_name',
|
||||
query,
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.product_variants.findAll({
|
||||
attributes: [ 'id', 'sku' ],
|
||||
const records = await db.reviews.findAll({
|
||||
attributes: [ 'id', 'author_name' ],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['sku', 'ASC']],
|
||||
orderBy: [['author_name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.sku,
|
||||
label: record.author_name,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -203,6 +203,9 @@ module.exports = class RolesDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
output.permissions = await roles.getPermissions({
|
||||
transaction
|
||||
});
|
||||
|
||||
@ -21,12 +21,12 @@ module.exports = class ShipmentsDBApi {
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
carrier: data.carrier
|
||||
tracking_number: data.tracking_number
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
tracking_number: data.tracking_number
|
||||
carrier: data.carrier
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -46,7 +46,7 @@ module.exports = class ShipmentsDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
cost: data.cost
|
||||
notes: data.notes
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -59,10 +59,6 @@ module.exports = class ShipmentsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await shipments.setOrder( data.order || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@ -80,12 +76,12 @@ module.exports = class ShipmentsDBApi {
|
||||
const shipmentsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
carrier: item.carrier
|
||||
tracking_number: item.tracking_number
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
tracking_number: item.tracking_number
|
||||
carrier: item.carrier
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -105,7 +101,7 @@ module.exports = class ShipmentsDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
cost: item.cost
|
||||
notes: item.notes
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -137,12 +133,12 @@ module.exports = class ShipmentsDBApi {
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.carrier !== undefined) updatePayload.carrier = data.carrier;
|
||||
|
||||
|
||||
if (data.tracking_number !== undefined) updatePayload.tracking_number = data.tracking_number;
|
||||
|
||||
|
||||
if (data.carrier !== undefined) updatePayload.carrier = data.carrier;
|
||||
|
||||
|
||||
if (data.status !== undefined) updatePayload.status = data.status;
|
||||
|
||||
|
||||
@ -152,7 +148,7 @@ module.exports = class ShipmentsDBApi {
|
||||
if (data.delivered_at !== undefined) updatePayload.delivered_at = data.delivered_at;
|
||||
|
||||
|
||||
if (data.cost !== undefined) updatePayload.cost = data.cost;
|
||||
if (data.notes !== undefined) updatePayload.notes = data.notes;
|
||||
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
@ -161,15 +157,6 @@ module.exports = class ShipmentsDBApi {
|
||||
|
||||
|
||||
|
||||
if (data.order !== undefined) {
|
||||
await shipments.setOrder(
|
||||
|
||||
data.order,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -256,9 +243,7 @@ module.exports = class ShipmentsDBApi {
|
||||
|
||||
|
||||
|
||||
output.order = await shipments.getOrder({
|
||||
transaction
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@ -286,23 +271,6 @@ module.exports = class ShipmentsDBApi {
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
model: db.orders,
|
||||
as: 'order',
|
||||
|
||||
where: filter.order ? {
|
||||
[Op.or]: [
|
||||
{ id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } },
|
||||
{
|
||||
order_number: {
|
||||
[Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||
}
|
||||
},
|
||||
]
|
||||
} : {},
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
];
|
||||
@ -316,6 +284,17 @@ module.exports = class ShipmentsDBApi {
|
||||
}
|
||||
|
||||
|
||||
if (filter.tracking_number) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'shipments',
|
||||
'tracking_number',
|
||||
filter.tracking_number,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.carrier) {
|
||||
where = {
|
||||
...where,
|
||||
@ -327,13 +306,13 @@ module.exports = class ShipmentsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.tracking_number) {
|
||||
if (filter.notes) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'shipments',
|
||||
'tracking_number',
|
||||
filter.tracking_number,
|
||||
'notes',
|
||||
filter.notes,
|
||||
),
|
||||
};
|
||||
}
|
||||
@ -409,30 +388,6 @@ module.exports = class ShipmentsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.costRange) {
|
||||
const [start, end] = filter.costRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
cost: {
|
||||
...where.cost,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
cost: {
|
||||
...where.cost,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
@ -451,8 +406,6 @@ module.exports = class ShipmentsDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
|
||||
@ -31,12 +31,12 @@ module.exports = class SuppliersDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
phone: data.phone
|
||||
email: data.email
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
email: data.email
|
||||
phone: data.phone
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -86,12 +86,12 @@ module.exports = class SuppliersDBApi {
|
||||
null
|
||||
,
|
||||
|
||||
phone: item.phone
|
||||
email: item.email
|
||||
||
|
||||
null
|
||||
,
|
||||
|
||||
email: item.email
|
||||
phone: item.phone
|
||||
||
|
||||
null
|
||||
,
|
||||
@ -139,12 +139,12 @@ module.exports = class SuppliersDBApi {
|
||||
if (data.contact_name !== undefined) updatePayload.contact_name = data.contact_name;
|
||||
|
||||
|
||||
if (data.phone !== undefined) updatePayload.phone = data.phone;
|
||||
|
||||
|
||||
if (data.email !== undefined) updatePayload.email = data.email;
|
||||
|
||||
|
||||
if (data.phone !== undefined) updatePayload.phone = data.phone;
|
||||
|
||||
|
||||
if (data.address !== undefined) updatePayload.address = data.address;
|
||||
|
||||
|
||||
@ -235,7 +235,8 @@ module.exports = class SuppliersDBApi {
|
||||
|
||||
|
||||
|
||||
output.product_variants_supplier = await suppliers.getProduct_variants_supplier({
|
||||
|
||||
output.products_supplier = await suppliers.getProducts_supplier({
|
||||
transaction
|
||||
});
|
||||
|
||||
@ -247,6 +248,8 @@ module.exports = class SuppliersDBApi {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return output;
|
||||
}
|
||||
@ -307,17 +310,6 @@ module.exports = class SuppliersDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.phone) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'suppliers',
|
||||
'phone',
|
||||
filter.phone,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.email) {
|
||||
where = {
|
||||
...where,
|
||||
@ -329,6 +321,17 @@ module.exports = class SuppliersDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.phone) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'suppliers',
|
||||
'phone',
|
||||
filter.phone,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.address) {
|
||||
where = {
|
||||
...where,
|
||||
|
||||
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',
|
||||
dialect: 'postgres',
|
||||
password: '',
|
||||
database: 'db_storeops_manager',
|
||||
database: 'db_store_operations_manager',
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
logging: console.log,
|
||||
seederStorage: 'sequelize',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -52,6 +52,8 @@ description: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
db.categories.hasMany(db.products, {
|
||||
as: 'products_category',
|
||||
foreignKey: {
|
||||
@ -69,6 +71,7 @@ description: {
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
@ -35,14 +35,14 @@ phone: {
|
||||
|
||||
},
|
||||
|
||||
billing_address: {
|
||||
company: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
shipping_address: {
|
||||
address: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
@ -56,11 +56,8 @@ notes: {
|
||||
|
||||
},
|
||||
|
||||
vip: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
loyalty_points: {
|
||||
type: DataTypes.INTEGER,
|
||||
|
||||
|
||||
|
||||
@ -93,6 +90,9 @@ vip: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
db.customers.hasMany(db.orders, {
|
||||
as: 'orders_customer',
|
||||
foreignKey: {
|
||||
|
||||
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');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const inventory = sequelize.define(
|
||||
'inventory',
|
||||
const inventory_movements = sequelize.define(
|
||||
'inventory_movements',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
@ -14,44 +14,44 @@ module.exports = function(sequelize, DataTypes) {
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
quantity: {
|
||||
note: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
change: {
|
||||
type: DataTypes.INTEGER,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
location: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
change_type: {
|
||||
reason: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
|
||||
|
||||
values: [
|
||||
|
||||
"addition",
|
||||
"Purchase",
|
||||
|
||||
|
||||
"deduction",
|
||||
"Sale",
|
||||
|
||||
|
||||
"adjustment",
|
||||
"Return",
|
||||
|
||||
|
||||
"transfer"
|
||||
"Adjustment"
|
||||
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
note: {
|
||||
type: DataTypes.TEXT,
|
||||
date: {
|
||||
type: DataTypes.DATE,
|
||||
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ note: {
|
||||
},
|
||||
);
|
||||
|
||||
inventory.associate = (db) => {
|
||||
inventory_movements.associate = (db) => {
|
||||
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
@ -90,14 +90,17 @@ note: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
db.inventory.belongsTo(db.product_variants, {
|
||||
as: 'variant',
|
||||
db.inventory_movements.belongsTo(db.products, {
|
||||
as: 'product',
|
||||
foreignKey: {
|
||||
name: 'variantId',
|
||||
name: 'productId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
@ -105,18 +108,18 @@ note: {
|
||||
|
||||
|
||||
|
||||
db.inventory.belongsTo(db.users, {
|
||||
db.inventory_movements.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.inventory.belongsTo(db.users, {
|
||||
db.inventory_movements.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
return inventory;
|
||||
return inventory_movements;
|
||||
};
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ module.exports = function(sequelize, DataTypes) {
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
product_name: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
@ -40,6 +40,13 @@ total: {
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
sku: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
importHash: {
|
||||
@ -75,22 +82,17 @@ total: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
db.order_items.belongsTo(db.orders, {
|
||||
as: 'order',
|
||||
db.order_items.belongsTo(db.products, {
|
||||
as: 'product',
|
||||
foreignKey: {
|
||||
name: 'orderId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.order_items.belongsTo(db.product_variants, {
|
||||
as: 'product_variant',
|
||||
foreignKey: {
|
||||
name: 'product_variantId',
|
||||
name: 'productId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
@ -49,6 +49,41 @@ status: {
|
||||
|
||||
},
|
||||
|
||||
order_date: {
|
||||
type: DataTypes.DATE,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
delivery_date: {
|
||||
type: DataTypes.DATE,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
subtotal: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
tax: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
shipping_fee: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
total: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
@ -56,47 +91,18 @@ total: {
|
||||
|
||||
},
|
||||
|
||||
placed_at: {
|
||||
type: DataTypes.DATE,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
shipped_at: {
|
||||
type: DataTypes.DATE,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
payment_status: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
|
||||
|
||||
values: [
|
||||
|
||||
"Paid",
|
||||
|
||||
|
||||
"Unpaid",
|
||||
|
||||
|
||||
"PartiallyRefunded",
|
||||
|
||||
|
||||
"Refunded"
|
||||
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
shipping_address: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
billing_address: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
importHash: {
|
||||
@ -114,6 +120,60 @@ shipping_address: {
|
||||
|
||||
orders.associate = (db) => {
|
||||
|
||||
db.orders.belongsToMany(db.order_items, {
|
||||
as: 'items',
|
||||
foreignKey: {
|
||||
name: 'orders_itemsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'ordersItemsOrder_items',
|
||||
});
|
||||
|
||||
db.orders.belongsToMany(db.order_items, {
|
||||
as: 'items_filter',
|
||||
foreignKey: {
|
||||
name: 'orders_itemsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'ordersItemsOrder_items',
|
||||
});
|
||||
|
||||
db.orders.belongsToMany(db.payments, {
|
||||
as: 'payments',
|
||||
foreignKey: {
|
||||
name: 'orders_paymentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'ordersPaymentsPayments',
|
||||
});
|
||||
|
||||
db.orders.belongsToMany(db.payments, {
|
||||
as: 'payments_filter',
|
||||
foreignKey: {
|
||||
name: 'orders_paymentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'ordersPaymentsPayments',
|
||||
});
|
||||
|
||||
db.orders.belongsToMany(db.shipments, {
|
||||
as: 'shipments',
|
||||
foreignKey: {
|
||||
name: 'orders_shipmentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'ordersShipmentsShipments',
|
||||
});
|
||||
|
||||
db.orders.belongsToMany(db.shipments, {
|
||||
as: 'shipments_filter',
|
||||
foreignKey: {
|
||||
name: 'orders_shipmentsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'ordersShipmentsShipments',
|
||||
});
|
||||
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
@ -127,8 +187,14 @@ shipping_address: {
|
||||
|
||||
|
||||
|
||||
db.orders.hasMany(db.order_items, {
|
||||
as: 'order_items_order',
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
db.orders.hasMany(db.returns, {
|
||||
as: 'returns_order',
|
||||
foreignKey: {
|
||||
name: 'orderId',
|
||||
},
|
||||
@ -136,25 +202,6 @@ shipping_address: {
|
||||
});
|
||||
|
||||
|
||||
db.orders.hasMany(db.payments, {
|
||||
as: 'payments_order',
|
||||
foreignKey: {
|
||||
name: 'orderId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
db.orders.hasMany(db.shipments, {
|
||||
as: 'shipments_order',
|
||||
foreignKey: {
|
||||
name: 'orderId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
@ -168,6 +215,14 @@ shipping_address: {
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.orders.belongsTo(db.users, {
|
||||
as: 'assigned_to',
|
||||
foreignKey: {
|
||||
name: 'assigned_toId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -14,14 +14,21 @@ module.exports = function(sequelize, DataTypes) {
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
transaction_id: {
|
||||
transaction_ref: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
payment_method: {
|
||||
amount: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
method: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
|
||||
@ -43,13 +50,6 @@ payment_method: {
|
||||
|
||||
},
|
||||
|
||||
amount: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
status: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
@ -57,15 +57,15 @@ status: {
|
||||
|
||||
values: [
|
||||
|
||||
"Succeeded",
|
||||
"Pending",
|
||||
|
||||
|
||||
"Completed",
|
||||
|
||||
|
||||
"Failed",
|
||||
|
||||
|
||||
"Pending",
|
||||
|
||||
|
||||
"Refunded"
|
||||
|
||||
],
|
||||
@ -77,6 +77,23 @@ paid_at: {
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
provider: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
refunded: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
importHash: {
|
||||
@ -112,21 +129,26 @@ paid_at: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
db.payments.belongsTo(db.orders, {
|
||||
as: 'order',
|
||||
foreignKey: {
|
||||
name: 'orderId',
|
||||
},
|
||||
|
||||
|
||||
db.payments.hasMany(db.file, {
|
||||
as: 'receipt',
|
||||
foreignKey: 'belongsToId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
belongsTo: db.payments.getTableName(),
|
||||
belongsToColumn: 'receipt',
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
db.payments.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
@ -54,6 +54,9 @@ name: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
@ -42,11 +42,25 @@ price: {
|
||||
|
||||
},
|
||||
|
||||
weight: {
|
||||
cost: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
currency: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
stock: {
|
||||
type: DataTypes.INTEGER,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
active: {
|
||||
@ -74,6 +88,24 @@ active: {
|
||||
|
||||
products.associate = (db) => {
|
||||
|
||||
db.products.belongsToMany(db.tags, {
|
||||
as: 'tags',
|
||||
foreignKey: {
|
||||
name: 'products_tagsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'productsTagsTags',
|
||||
});
|
||||
|
||||
db.products.belongsToMany(db.tags, {
|
||||
as: 'tags_filter',
|
||||
foreignKey: {
|
||||
name: 'products_tagsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'productsTagsTags',
|
||||
});
|
||||
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
@ -84,8 +116,19 @@ active: {
|
||||
|
||||
|
||||
|
||||
db.products.hasMany(db.product_variants, {
|
||||
as: 'product_variants_product',
|
||||
|
||||
|
||||
db.products.hasMany(db.inventory_movements, {
|
||||
as: 'inventory_movements_product',
|
||||
foreignKey: {
|
||||
name: 'productId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
db.products.hasMany(db.reviews, {
|
||||
as: 'reviews_product',
|
||||
foreignKey: {
|
||||
name: 'productId',
|
||||
},
|
||||
@ -95,6 +138,14 @@ active: {
|
||||
|
||||
|
||||
|
||||
db.products.hasMany(db.order_items, {
|
||||
as: 'order_items_product',
|
||||
foreignKey: {
|
||||
name: 'productId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@ -112,6 +163,14 @@ active: {
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.products.belongsTo(db.suppliers, {
|
||||
as: 'supplier',
|
||||
foreignKey: {
|
||||
name: 'supplierId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
db.products.hasMany(db.file, {
|
||||
|
||||
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');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const product_variants = sequelize.define(
|
||||
'product_variants',
|
||||
const reviews = sequelize.define(
|
||||
'reviews',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
@ -14,42 +14,35 @@ module.exports = function(sequelize, DataTypes) {
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
sku: {
|
||||
author_name: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
title: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
price: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
stock: {
|
||||
rating: {
|
||||
type: DataTypes.INTEGER,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
attributes: {
|
||||
comment: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
active: {
|
||||
created: {
|
||||
type: DataTypes.DATE,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
approved: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
|
||||
allowNull: false,
|
||||
@ -72,7 +65,7 @@ active: {
|
||||
},
|
||||
);
|
||||
|
||||
product_variants.associate = (db) => {
|
||||
reviews.associate = (db) => {
|
||||
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
@ -85,23 +78,10 @@ active: {
|
||||
|
||||
|
||||
|
||||
db.product_variants.hasMany(db.inventory, {
|
||||
as: 'inventory_variant',
|
||||
foreignKey: {
|
||||
name: 'variantId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
db.product_variants.hasMany(db.order_items, {
|
||||
as: 'order_items_product_variant',
|
||||
foreignKey: {
|
||||
name: 'product_variantId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@ -112,7 +92,7 @@ active: {
|
||||
|
||||
|
||||
|
||||
db.product_variants.belongsTo(db.products, {
|
||||
db.reviews.belongsTo(db.products, {
|
||||
as: 'product',
|
||||
foreignKey: {
|
||||
name: 'productId',
|
||||
@ -120,39 +100,21 @@ active: {
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.product_variants.belongsTo(db.suppliers, {
|
||||
as: 'supplier',
|
||||
foreignKey: {
|
||||
name: 'supplierId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
db.product_variants.hasMany(db.file, {
|
||||
as: 'images',
|
||||
foreignKey: 'belongsToId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
belongsTo: db.product_variants.getTableName(),
|
||||
belongsToColumn: 'images',
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
db.product_variants.belongsTo(db.users, {
|
||||
db.reviews.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.product_variants.belongsTo(db.users, {
|
||||
db.reviews.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
return product_variants;
|
||||
return reviews;
|
||||
};
|
||||
|
||||
|
||||
@ -87,6 +87,9 @@ role_customization: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
@ -14,14 +14,14 @@ module.exports = function(sequelize, DataTypes) {
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
carrier: {
|
||||
tracking_number: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
tracking_number: {
|
||||
carrier: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ status: {
|
||||
|
||||
values: [
|
||||
|
||||
"Pending",
|
||||
"LabelCreated",
|
||||
|
||||
|
||||
"InTransit",
|
||||
@ -64,8 +64,8 @@ delivered_at: {
|
||||
|
||||
},
|
||||
|
||||
cost: {
|
||||
type: DataTypes.DECIMAL,
|
||||
notes: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
@ -104,18 +104,13 @@ cost: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
db.shipments.belongsTo(db.orders, {
|
||||
as: 'order',
|
||||
foreignKey: {
|
||||
name: 'orderId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -28,14 +28,14 @@ contact_name: {
|
||||
|
||||
},
|
||||
|
||||
phone: {
|
||||
email: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
email: {
|
||||
phone: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
@ -81,8 +81,9 @@ notes: {
|
||||
|
||||
|
||||
|
||||
db.suppliers.hasMany(db.product_variants, {
|
||||
as: 'product_variants_supplier',
|
||||
|
||||
db.suppliers.hasMany(db.products, {
|
||||
as: 'products_supplier',
|
||||
foreignKey: {
|
||||
name: 'supplierId',
|
||||
},
|
||||
@ -97,6 +98,8 @@ notes: {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
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 searchRoutes = require('./routes/search');
|
||||
const pexelsRoutes = require('./routes/pexels');
|
||||
const dashboardRoutes = require('./routes/dashboard');
|
||||
|
||||
const openaiRoutes = require('./routes/openai');
|
||||
|
||||
@ -30,11 +31,17 @@ const customersRoutes = require('./routes/customers');
|
||||
|
||||
const categoriesRoutes = require('./routes/categories');
|
||||
|
||||
const suppliersRoutes = require('./routes/suppliers');
|
||||
|
||||
const tagsRoutes = require('./routes/tags');
|
||||
|
||||
const productsRoutes = require('./routes/products');
|
||||
|
||||
const product_variantsRoutes = require('./routes/product_variants');
|
||||
const inventory_movementsRoutes = require('./routes/inventory_movements');
|
||||
|
||||
const inventoryRoutes = require('./routes/inventory');
|
||||
const reviewsRoutes = require('./routes/reviews');
|
||||
|
||||
const discountsRoutes = require('./routes/discounts');
|
||||
|
||||
const ordersRoutes = require('./routes/orders');
|
||||
|
||||
@ -44,7 +51,7 @@ const paymentsRoutes = require('./routes/payments');
|
||||
|
||||
const shipmentsRoutes = require('./routes/shipments');
|
||||
|
||||
const suppliersRoutes = require('./routes/suppliers');
|
||||
const returnsRoutes = require('./routes/returns');
|
||||
|
||||
|
||||
const getBaseUrl = (url) => {
|
||||
@ -57,8 +64,8 @@ const options = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
version: "1.0.0",
|
||||
title: "StoreOps Manager",
|
||||
description: "StoreOps Manager Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.",
|
||||
title: "Store Operations Manager",
|
||||
description: "Store Operations Manager Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.",
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
@ -101,6 +108,7 @@ app.use(bodyParser.json());
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/file', fileRoutes);
|
||||
app.use('/api/pexels', pexelsRoutes);
|
||||
app.use('/api/dashboard', passport.authenticate('jwt', {session: false}), dashboardRoutes);
|
||||
app.enable('trust proxy');
|
||||
|
||||
|
||||
@ -114,11 +122,17 @@ app.use('/api/customers', passport.authenticate('jwt', {session: false}), custom
|
||||
|
||||
app.use('/api/categories', passport.authenticate('jwt', {session: false}), categoriesRoutes);
|
||||
|
||||
app.use('/api/suppliers', passport.authenticate('jwt', {session: false}), suppliersRoutes);
|
||||
|
||||
app.use('/api/tags', passport.authenticate('jwt', {session: false}), tagsRoutes);
|
||||
|
||||
app.use('/api/products', passport.authenticate('jwt', {session: false}), productsRoutes);
|
||||
|
||||
app.use('/api/product_variants', passport.authenticate('jwt', {session: false}), product_variantsRoutes);
|
||||
app.use('/api/inventory_movements', passport.authenticate('jwt', {session: false}), inventory_movementsRoutes);
|
||||
|
||||
app.use('/api/inventory', passport.authenticate('jwt', {session: false}), inventoryRoutes);
|
||||
app.use('/api/reviews', passport.authenticate('jwt', {session: false}), reviewsRoutes);
|
||||
|
||||
app.use('/api/discounts', passport.authenticate('jwt', {session: false}), discountsRoutes);
|
||||
|
||||
app.use('/api/orders', passport.authenticate('jwt', {session: false}), ordersRoutes);
|
||||
|
||||
@ -128,7 +142,7 @@ app.use('/api/payments', passport.authenticate('jwt', {session: false}), payment
|
||||
|
||||
app.use('/api/shipments', passport.authenticate('jwt', {session: false}), shipmentsRoutes);
|
||||
|
||||
app.use('/api/suppliers', passport.authenticate('jwt', {session: false}), suppliersRoutes);
|
||||
app.use('/api/returns', passport.authenticate('jwt', {session: false}), returnsRoutes);
|
||||
|
||||
app.use(
|
||||
'/api/openai',
|
||||
|
||||
@ -26,6 +26,12 @@ router.use(checkCrudPermissions('categories'));
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
* description:
|
||||
* type: string
|
||||
* default: description
|
||||
|
||||
|
||||
|
||||
@ -285,7 +291,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
const fields = ['id','name','description',
|
||||
|
||||
|
||||
|
||||
|
||||
@ -26,7 +26,28 @@ router.use(checkCrudPermissions('customers'));
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
* email:
|
||||
* type: string
|
||||
* default: email
|
||||
* phone:
|
||||
* type: string
|
||||
* default: phone
|
||||
* company:
|
||||
* type: string
|
||||
* default: company
|
||||
* address:
|
||||
* type: string
|
||||
* default: address
|
||||
* notes:
|
||||
* type: string
|
||||
* default: notes
|
||||
|
||||
* loyalty_points:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
|
||||
*/
|
||||
@ -285,8 +306,8 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
|
||||
const fields = ['id','name','email','phone','company','address','notes',
|
||||
'loyalty_points',
|
||||
|
||||
|
||||
];
|
||||
|
||||
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 Product_variantsService = require('../services/product_variants');
|
||||
const Product_variantsDBApi = require('../db/api/product_variants');
|
||||
const DiscountsService = require('../services/discounts');
|
||||
const DiscountsDBApi = require('../db/api/discounts');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
|
||||
@ -15,36 +15,46 @@ const {
|
||||
checkCrudPermissions,
|
||||
} = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('product_variants'));
|
||||
router.use(checkCrudPermissions('discounts'));
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Product_variants:
|
||||
* Discounts:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* code:
|
||||
* type: string
|
||||
* default: code
|
||||
* description:
|
||||
* type: string
|
||||
* default: description
|
||||
|
||||
|
||||
* value:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Product_variants
|
||||
* description: The Product_variants managing API
|
||||
* name: Discounts
|
||||
* description: The Discounts managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/product_variants:
|
||||
* /api/discounts:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* tags: [Discounts]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
@ -56,14 +66,14 @@ router.use(checkCrudPermissions('product_variants'));
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
@ -74,7 +84,7 @@ router.use(checkCrudPermissions('product_variants'));
|
||||
router.post('/', wrapAsync(async (req, res) => {
|
||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Product_variantsService.create(req.body.data, req.currentUser, true, link.host);
|
||||
await DiscountsService.create(req.body.data, req.currentUser, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
@ -85,7 +95,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* tags: [Discounts]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
@ -98,14 +108,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
@ -117,18 +127,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await Product_variantsService.bulkImport(req, res, true, link.host);
|
||||
await DiscountsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/product_variants/{id}:
|
||||
* /api/discounts/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* tags: [Discounts]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
@ -151,7 +161,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
@ -160,7 +170,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -171,18 +181,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put('/:id', wrapAsync(async (req, res) => {
|
||||
await Product_variantsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
await DiscountsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/product_variants/{id}:
|
||||
* /api/discounts/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* tags: [Discounts]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
@ -198,7 +208,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -209,18 +219,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||
await Product_variantsService.remove(req.params.id, req.currentUser);
|
||||
await DiscountsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/product_variants/deleteByIds:
|
||||
* /api/discounts/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* tags: [Discounts]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
@ -238,7 +248,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -247,29 +257,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||
await Product_variantsService.deleteByIds(req.body.data, req.currentUser);
|
||||
await DiscountsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/product_variants:
|
||||
* /api/discounts:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* summary: Get all product_variants
|
||||
* description: Get all product_variants
|
||||
* tags: [Discounts]
|
||||
* summary: Get all discounts
|
||||
* description: Get all discounts
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Product_variants list successfully received
|
||||
* description: Discounts list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -281,14 +291,14 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Product_variantsDBApi.findAll(
|
||||
const payload = await DiscountsDBApi.findAll(
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
const fields = ['id','code','description',
|
||||
|
||||
|
||||
|
||||
'value',
|
||||
'starts','ends',
|
||||
];
|
||||
const opts = { fields };
|
||||
try {
|
||||
@ -307,22 +317,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/product_variants/count:
|
||||
* /api/discounts/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* summary: Count all product_variants
|
||||
* description: Count all product_variants
|
||||
* tags: [Discounts]
|
||||
* summary: Count all discounts
|
||||
* description: Count all discounts
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Product_variants count successfully received
|
||||
* description: Discounts count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -333,7 +343,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
router.get('/count', wrapAsync(async (req, res) => {
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await Product_variantsDBApi.findAll(
|
||||
const payload = await DiscountsDBApi.findAll(
|
||||
req.query,
|
||||
null,
|
||||
{ countOnly: true, currentUser }
|
||||
@ -344,22 +354,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/product_variants/autocomplete:
|
||||
* /api/discounts/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* summary: Find all product_variants that match search criteria
|
||||
* description: Find all product_variants that match search criteria
|
||||
* tags: [Discounts]
|
||||
* summary: Find all discounts that match search criteria
|
||||
* description: Find all discounts that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Product_variants list successfully received
|
||||
* description: Discounts list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -369,7 +379,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const payload = await Product_variantsDBApi.findAllAutocomplete(
|
||||
const payload = await DiscountsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
@ -381,11 +391,11 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/product_variants/{id}:
|
||||
* /api/discounts/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Product_variants]
|
||||
* tags: [Discounts]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
@ -401,7 +411,7 @@ router.get('/autocomplete', async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Product_variants"
|
||||
* $ref: "#/components/schemas/Discounts"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -412,7 +422,7 @@ router.get('/autocomplete', async (req, res) => {
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await Product_variantsDBApi.findBy(
|
||||
const payload = await DiscountsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
);
|
||||
|
||||
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
|
||||
* properties:
|
||||
|
||||
* product_name:
|
||||
* type: string
|
||||
* default: product_name
|
||||
* sku:
|
||||
* type: string
|
||||
* default: sku
|
||||
|
||||
* quantity:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
* unit_price:
|
||||
* type: integer
|
||||
* format: int64
|
||||
* total:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*/
|
||||
|
||||
@ -285,9 +300,9 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
|
||||
|
||||
const fields = ['id','product_name','sku',
|
||||
'quantity',
|
||||
'unit_price','total',
|
||||
|
||||
];
|
||||
const opts = { fields };
|
||||
|
||||
@ -26,9 +26,31 @@ router.use(checkCrudPermissions('orders'));
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* order_number:
|
||||
* type: string
|
||||
* default: order_number
|
||||
* shipping_address:
|
||||
* type: string
|
||||
* default: shipping_address
|
||||
* billing_address:
|
||||
* type: string
|
||||
* default: billing_address
|
||||
|
||||
|
||||
* subtotal:
|
||||
* type: integer
|
||||
* format: int64
|
||||
* tax:
|
||||
* type: integer
|
||||
* format: int64
|
||||
* shipping_fee:
|
||||
* type: integer
|
||||
* format: int64
|
||||
* total:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -285,10 +307,10 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
const fields = ['id','order_number','shipping_address','billing_address',
|
||||
|
||||
|
||||
|
||||
'subtotal','tax','shipping_fee','total',
|
||||
'order_date','delivery_date',
|
||||
];
|
||||
const opts = { fields };
|
||||
try {
|
||||
|
||||
@ -26,9 +26,20 @@ router.use(checkCrudPermissions('payments'));
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* transaction_ref:
|
||||
* type: string
|
||||
* default: transaction_ref
|
||||
* provider:
|
||||
* type: string
|
||||
* default: provider
|
||||
|
||||
|
||||
* amount:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -285,10 +296,10 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
const fields = ['id','transaction_ref','provider',
|
||||
|
||||
|
||||
|
||||
'amount',
|
||||
'paid_at',
|
||||
];
|
||||
const opts = { fields };
|
||||
try {
|
||||
|
||||
@ -26,8 +26,29 @@ router.use(checkCrudPermissions('products'));
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
* sku:
|
||||
* type: string
|
||||
* default: sku
|
||||
* description:
|
||||
* type: string
|
||||
* default: description
|
||||
* currency:
|
||||
* type: string
|
||||
* default: currency
|
||||
|
||||
* stock:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
* price:
|
||||
* type: integer
|
||||
* format: int64
|
||||
* cost:
|
||||
* type: integer
|
||||
* format: int64
|
||||
|
||||
*/
|
||||
|
||||
@ -285,9 +306,9 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
|
||||
|
||||
const fields = ['id','name','sku','description','currency',
|
||||
'stock',
|
||||
'price','cost',
|
||||
|
||||
];
|
||||
const opts = { fields };
|
||||
|
||||
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
|
||||
* properties:
|
||||
|
||||
* tracking_number:
|
||||
* type: string
|
||||
* default: tracking_number
|
||||
* carrier:
|
||||
* type: string
|
||||
* default: carrier
|
||||
* notes:
|
||||
* type: string
|
||||
* default: notes
|
||||
|
||||
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -285,10 +295,10 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
const fields = ['id','tracking_number','carrier','notes',
|
||||
|
||||
|
||||
|
||||
'shipped_at','delivered_at',
|
||||
];
|
||||
const opts = { fields };
|
||||
try {
|
||||
|
||||
@ -26,6 +26,24 @@ router.use(checkCrudPermissions('suppliers'));
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
* contact_name:
|
||||
* type: string
|
||||
* default: contact_name
|
||||
* email:
|
||||
* type: string
|
||||
* default: email
|
||||
* phone:
|
||||
* type: string
|
||||
* default: phone
|
||||
* address:
|
||||
* type: string
|
||||
* default: address
|
||||
* notes:
|
||||
* type: string
|
||||
* default: notes
|
||||
|
||||
|
||||
|
||||
@ -285,7 +303,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
const fields = ['id','name','contact_name','email','phone','address','notes',
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
|
||||
const express = require('express');
|
||||
|
||||
const InventoryService = require('../services/inventory');
|
||||
const InventoryDBApi = require('../db/api/inventory');
|
||||
const TagsService = require('../services/tags');
|
||||
const TagsDBApi = require('../db/api/tags');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
|
||||
@ -15,17 +15,23 @@ const {
|
||||
checkCrudPermissions,
|
||||
} = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('inventory'));
|
||||
router.use(checkCrudPermissions('tags'));
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Inventory:
|
||||
* Tags:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
* color:
|
||||
* type: string
|
||||
* default: color
|
||||
|
||||
|
||||
|
||||
@ -34,17 +40,17 @@ router.use(checkCrudPermissions('inventory'));
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Inventory
|
||||
* description: The Inventory managing API
|
||||
* name: Tags
|
||||
* description: The Tags managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/inventory:
|
||||
* /api/tags:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* tags: [Tags]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
@ -56,14 +62,14 @@ router.use(checkCrudPermissions('inventory'));
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
@ -74,7 +80,7 @@ router.use(checkCrudPermissions('inventory'));
|
||||
router.post('/', wrapAsync(async (req, res) => {
|
||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await InventoryService.create(req.body.data, req.currentUser, true, link.host);
|
||||
await TagsService.create(req.body.data, req.currentUser, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
@ -85,7 +91,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* tags: [Tags]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
@ -98,14 +104,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
@ -117,18 +123,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await InventoryService.bulkImport(req, res, true, link.host);
|
||||
await TagsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/inventory/{id}:
|
||||
* /api/tags/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* tags: [Tags]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
@ -151,7 +157,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
@ -160,7 +166,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -171,18 +177,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
* description: Some server error
|
||||
*/
|
||||
router.put('/:id', wrapAsync(async (req, res) => {
|
||||
await InventoryService.update(req.body.data, req.body.id, req.currentUser);
|
||||
await TagsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/inventory/{id}:
|
||||
* /api/tags/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* tags: [Tags]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
@ -198,7 +204,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -209,18 +215,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
||||
* description: Some server error
|
||||
*/
|
||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||
await InventoryService.remove(req.params.id, req.currentUser);
|
||||
await TagsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/inventory/deleteByIds:
|
||||
* /api/tags/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* tags: [Tags]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
@ -238,7 +244,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -247,29 +253,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||
await InventoryService.deleteByIds(req.body.data, req.currentUser);
|
||||
await TagsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/inventory:
|
||||
* /api/tags:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* summary: Get all inventory
|
||||
* description: Get all inventory
|
||||
* tags: [Tags]
|
||||
* summary: Get all tags
|
||||
* description: Get all tags
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Inventory list successfully received
|
||||
* description: Tags list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -281,11 +287,11 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await InventoryDBApi.findAll(
|
||||
const payload = await TagsDBApi.findAll(
|
||||
req.query, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id',
|
||||
const fields = ['id','name','color',
|
||||
|
||||
|
||||
|
||||
@ -307,22 +313,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/inventory/count:
|
||||
* /api/tags/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* summary: Count all inventory
|
||||
* description: Count all inventory
|
||||
* tags: [Tags]
|
||||
* summary: Count all tags
|
||||
* description: Count all tags
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Inventory count successfully received
|
||||
* description: Tags count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -333,7 +339,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
router.get('/count', wrapAsync(async (req, res) => {
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await InventoryDBApi.findAll(
|
||||
const payload = await TagsDBApi.findAll(
|
||||
req.query,
|
||||
null,
|
||||
{ countOnly: true, currentUser }
|
||||
@ -344,22 +350,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/inventory/autocomplete:
|
||||
* /api/tags/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* summary: Find all inventory that match search criteria
|
||||
* description: Find all inventory that match search criteria
|
||||
* tags: [Tags]
|
||||
* summary: Find all tags that match search criteria
|
||||
* description: Find all tags that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Inventory list successfully received
|
||||
* description: Tags list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -369,7 +375,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const payload = await InventoryDBApi.findAllAutocomplete(
|
||||
const payload = await TagsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
@ -381,11 +387,11 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/inventory/{id}:
|
||||
* /api/tags/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Inventory]
|
||||
* tags: [Tags]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
@ -401,7 +407,7 @@ router.get('/autocomplete', async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Inventory"
|
||||
* $ref: "#/components/schemas/Tags"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -412,7 +418,7 @@ router.get('/autocomplete', async (req, res) => {
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await InventoryDBApi.findBy(
|
||||
const payload = await TagsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const db = require('../db/models');
|
||||
const InventoryDBApi = require('../db/api/inventory');
|
||||
const DiscountsDBApi = require('../db/api/discounts');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
@ -11,11 +11,11 @@ const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
module.exports = class InventoryService {
|
||||
module.exports = class DiscountsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await InventoryDBApi.create(
|
||||
await DiscountsDBApi.create(
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
@ -51,7 +51,7 @@ module.exports = class InventoryService {
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await InventoryDBApi.bulkImport(results, {
|
||||
await DiscountsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
@ -68,18 +68,18 @@ module.exports = class InventoryService {
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let inventory = await InventoryDBApi.findBy(
|
||||
let discounts = await DiscountsDBApi.findBy(
|
||||
{id},
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!inventory) {
|
||||
if (!discounts) {
|
||||
throw new ValidationError(
|
||||
'inventoryNotFound',
|
||||
'discountsNotFound',
|
||||
);
|
||||
}
|
||||
|
||||
const updatedInventory = await InventoryDBApi.update(
|
||||
const updatedDiscounts = await DiscountsDBApi.update(
|
||||
id,
|
||||
data,
|
||||
{
|
||||
@ -89,7 +89,7 @@ module.exports = class InventoryService {
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
return updatedInventory;
|
||||
return updatedDiscounts;
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
@ -101,7 +101,7 @@ module.exports = class InventoryService {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await InventoryDBApi.deleteByIds(ids, {
|
||||
await DiscountsDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
@ -117,7 +117,7 @@ module.exports = class InventoryService {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await InventoryDBApi.remove(
|
||||
await DiscountsDBApi.remove(
|
||||
id,
|
||||
{
|
||||
currentUser,
|
||||
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 = {
|
||||
app: {
|
||||
title: 'StoreOps Manager',
|
||||
title: 'Store Operations Manager',
|
||||
},
|
||||
|
||||
auth: {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const db = require('../db/models');
|
||||
const Product_variantsDBApi = require('../db/api/product_variants');
|
||||
const ReturnsDBApi = require('../db/api/returns');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
@ -11,11 +11,11 @@ const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
module.exports = class Product_variantsService {
|
||||
module.exports = class ReturnsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await Product_variantsDBApi.create(
|
||||
await ReturnsDBApi.create(
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
@ -51,7 +51,7 @@ module.exports = class Product_variantsService {
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await Product_variantsDBApi.bulkImport(results, {
|
||||
await ReturnsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
@ -68,18 +68,18 @@ module.exports = class Product_variantsService {
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let product_variants = await Product_variantsDBApi.findBy(
|
||||
let returns = await ReturnsDBApi.findBy(
|
||||
{id},
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!product_variants) {
|
||||
if (!returns) {
|
||||
throw new ValidationError(
|
||||
'product_variantsNotFound',
|
||||
'returnsNotFound',
|
||||
);
|
||||
}
|
||||
|
||||
const updatedProduct_variants = await Product_variantsDBApi.update(
|
||||
const updatedReturns = await ReturnsDBApi.update(
|
||||
id,
|
||||
data,
|
||||
{
|
||||
@ -89,7 +89,7 @@ module.exports = class Product_variantsService {
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
return updatedProduct_variants;
|
||||
return updatedReturns;
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
@ -101,7 +101,7 @@ module.exports = class Product_variantsService {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Product_variantsDBApi.deleteByIds(ids, {
|
||||
await ReturnsDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
@ -117,7 +117,7 @@ module.exports = class Product_variantsService {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await Product_variantsDBApi.remove(
|
||||
await ReturnsDBApi.remove(
|
||||
id,
|
||||
{
|
||||
currentUser,
|
||||
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 = {
|
||||
@ -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
|
||||
environment:
|
||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||
- POSTGRES_DB=db_storeops_manager
|
||||
- POSTGRES_DB=db_store_operations_manager
|
||||
ports:
|
||||
- "5432:5432"
|
||||
logging:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# StoreOps Manager
|
||||
# Store Operations Manager
|
||||
|
||||
## This project was generated by Flatlogic Platform.
|
||||
## Install
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ColorButtonKey } from './interfaces'
|
||||
|
||||
export const gradientBgBase = 'bg-gradient-to-tr'
|
||||
export const colorBgBase = "bg-skyBlueTheme-mainBG"
|
||||
export const colorBgBase = "bg-violet-50/50"
|
||||
export const gradientBgPurplePink = `${gradientBgBase} from-purple-400 via-pink-500 to-red-500`
|
||||
export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}`
|
||||
export const gradientBgDark = `${gradientBgBase} from-dark-700 via-dark-900 to-dark-800`;
|
||||
@ -9,7 +9,7 @@ export const gradientBgPinkRed = `${gradientBgBase} from-pink-400 via-red-500 to
|
||||
|
||||
export const colorsBgLight = {
|
||||
white: 'bg-white text-black',
|
||||
light: ' bg-skyBlueTheme-outsideCardColor text-primaryText text-primaryText dark:bg-dark-900 dark:text-white',
|
||||
light: ' bg-white text-black text-black dark:bg-dark-900 dark:text-white',
|
||||
contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black',
|
||||
success: 'bg-emerald-500 border-emerald-500 dark:bg-pavitra-blue dark:border-pavitra-blue text-white',
|
||||
danger: 'bg-red-500 border-red-500 text-white',
|
||||
@ -19,7 +19,7 @@ export const colorsBgLight = {
|
||||
|
||||
export const colorsText = {
|
||||
white: 'text-black dark:text-slate-100',
|
||||
light: 'text-primaryText dark:text-slate-400',
|
||||
light: 'text-gray-700 dark:text-slate-400',
|
||||
contrast: 'dark:text-white',
|
||||
success: 'text-emerald-500',
|
||||
danger: 'text-red-500',
|
||||
@ -50,13 +50,13 @@ export const getButtonColor = (
|
||||
const colors = {
|
||||
ring: {
|
||||
white: 'ring-gray-200 dark:ring-gray-500',
|
||||
whiteDark: 'ring-skyBlueTheme-outsideCardColor dark:ring-dark-500',
|
||||
whiteDark: 'ring-gray-200 dark:ring-dark-500',
|
||||
lightDark: 'ring-gray-200 dark:ring-gray-500',
|
||||
contrast: 'ring-gray-300 dark:ring-gray-400',
|
||||
success: 'ring-emerald-300 dark:ring-pavitra-blue',
|
||||
danger: 'ring-red-300 dark:ring-red-700',
|
||||
warning: 'ring-yellow-300 dark:ring-yellow-700',
|
||||
info: "ring-skyBlueTheme-buttonColor dark:ring-pavitra-blue",
|
||||
info: "ring-blue-300 dark:ring-pavitra-blue",
|
||||
},
|
||||
active: {
|
||||
white: 'bg-gray-100',
|
||||
@ -66,21 +66,21 @@ export const getButtonColor = (
|
||||
success: 'bg-emerald-700 dark:bg-pavitra-blue',
|
||||
danger: 'bg-red-700 dark:bg-red-600',
|
||||
warning: 'bg-yellow-700 dark:bg-yellow-600',
|
||||
info: 'bg-skyBlueTheme-buttonColor dark:bg-pavitra-blue',
|
||||
info: 'bg-blue-700 dark:bg-pavitra-blue',
|
||||
},
|
||||
bg: {
|
||||
white: 'bg-white text-black',
|
||||
whiteDark: 'bg-skyBlueTheme-outsideCardColor text-primaryText dark:bg-dark-900 dark:text-white',
|
||||
whiteDark: 'bg-white text-black dark:bg-dark-900 dark:text-white',
|
||||
lightDark: 'bg-gray-100 text-black dark:bg-slate-800 dark:text-white',
|
||||
contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black',
|
||||
success: 'bg-emerald-600 dark:bg-pavitra-blue text-white',
|
||||
danger: 'bg-skyBlueTheme-outsideCardColor text-red-500 dark:text-white dark:bg-red-500 ',
|
||||
danger: 'bg-red-600 text-white dark:bg-red-500 ',
|
||||
warning: 'bg-yellow-600 dark:bg-yellow-500 text-white',
|
||||
info: " bg-skyBlueTheme-buttonColor dark:bg-pavitra-blue text-white ",
|
||||
info: " bg-blue-600 dark:bg-pavitra-blue text-white ",
|
||||
},
|
||||
bgHover: {
|
||||
white: 'hover:bg-gray-100',
|
||||
whiteDark: 'hover:bg-skyBlueTheme-outsideCardColor hover:dark:bg-dark-800',
|
||||
whiteDark: 'hover:bg-gray-100 hover:dark:bg-dark-800',
|
||||
lightDark: 'hover:bg-gray-200 hover:dark:bg-slate-700',
|
||||
contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100',
|
||||
success:
|
||||
@ -89,24 +89,24 @@ export const getButtonColor = (
|
||||
'hover:bg-red-700 hover:border-red-700 hover:dark:bg-red-600 hover:dark:border-red-600',
|
||||
warning:
|
||||
'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600',
|
||||
info: "hover:bg-skyBlueTheme-800 hover:border-skyBlueTheme-buttonColor hover:dark:bg-pavitra-blue/80 hover:dark:border-pavitra-blue/80",
|
||||
info: "hover:bg-blue-700 hover:border-blue-700 hover:dark:bg-pavitra-blue/80 hover:dark:border-pavitra-blue/80",
|
||||
},
|
||||
borders: {
|
||||
white: 'border-white',
|
||||
whiteDark: 'border-skyBlueTheme-outsideCardColor dark:border-dark-900',
|
||||
whiteDark: 'border-white dark:border-dark-900',
|
||||
lightDark: 'border-gray-100 dark:border-slate-800',
|
||||
contrast: 'border-gray-800 dark:border-white',
|
||||
success: 'border-emerald-600 dark:border-pavitra-blue',
|
||||
danger: 'border-red-600 dark:border-red-500',
|
||||
warning: 'border-yellow-600 dark:border-yellow-500',
|
||||
info: "border-skyBlueTheme-buttonColor border-blue-600 dark:border-pavitra-blue",
|
||||
info: "border-blue-600 border-blue-600 dark:border-pavitra-blue",
|
||||
},
|
||||
text: {
|
||||
contrast: 'dark:text-slate-100',
|
||||
success: 'text-emerald-600 dark:text-pavitra-blue',
|
||||
danger: 'text-red-600 dark:text-red-500',
|
||||
warning: 'text-yellow-600 dark:text-yellow-500',
|
||||
info: ' dark:text-pavitra-blue',
|
||||
info: 'text-blue-600 dark:text-pavitra-blue',
|
||||
},
|
||||
outlineHover: {
|
||||
contrast:
|
||||
@ -116,7 +116,7 @@ export const getButtonColor = (
|
||||
'hover:bg-red-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-red-600',
|
||||
warning:
|
||||
'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600',
|
||||
info: "hover:bg-skyBlueTheme-buttonColor text-skyBlueTheme-buttonColor hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue",
|
||||
info: "hover:bg-blue-600 hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -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">
|
||||
|
||||
<b className="font-black">StoreOps Manager</b>
|
||||
<b className="font-black">Store Operations Manager</b>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@ -76,6 +76,42 @@ const CardCategories = ({
|
||||
</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'>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>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@ -37,7 +37,7 @@ const ListCategories = ({ categories, loading, onDelete, currentPage, numPages,
|
||||
{!loading && categories.map((item) => (
|
||||
<div key={item.id}>
|
||||
<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
|
||||
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>
|
||||
<ListActionsPopover
|
||||
onDelete={onDelete}
|
||||
|
||||
@ -18,7 +18,7 @@ import dataFormatter from '../../helpers/dataFormatter'
|
||||
import {dataGridStyles} from "../../styles";
|
||||
|
||||
|
||||
import CardCategories from './CardCategories';
|
||||
import ListCategories from './ListCategories';
|
||||
|
||||
|
||||
const perPage = 10
|
||||
@ -414,13 +414,13 @@ const TableSampleCategories = ({ filterItems, setFilterItems, filters, showGrid
|
||||
<div className="flex">
|
||||
<BaseButton
|
||||
className="my-2 mr-3"
|
||||
type='submit' color='info'
|
||||
color="success"
|
||||
label='Apply'
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<BaseButton
|
||||
className="my-2"
|
||||
type='reset' color='info' outline
|
||||
color='info'
|
||||
label='Cancel'
|
||||
onClick={handleReset}
|
||||
/>
|
||||
@ -443,7 +443,7 @@ const TableSampleCategories = ({ filterItems, setFilterItems, filters, showGrid
|
||||
|
||||
|
||||
{categories && Array.isArray(categories) && !showGrid && (
|
||||
<CardCategories
|
||||
<ListCategories
|
||||
categories={categories}
|
||||
loading={loading}
|
||||
onDelete={handleDeleteModalAction}
|
||||
|
||||
@ -41,6 +41,58 @@ export const loadColumns = async (
|
||||
|
||||
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',
|
||||
type: 'actions',
|
||||
|
||||
@ -76,6 +76,90 @@ const CardCustomers = ({
|
||||
</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'>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>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@ -37,7 +37,7 @@ const ListCustomers = ({ customers, loading, onDelete, currentPage, numPages, on
|
||||
{!loading && customers.map((item) => (
|
||||
<div key={item.id}>
|
||||
<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
|
||||
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>
|
||||
<ListActionsPopover
|
||||
onDelete={onDelete}
|
||||
|
||||
@ -18,8 +18,6 @@ import dataFormatter from '../../helpers/dataFormatter'
|
||||
import {dataGridStyles} from "../../styles";
|
||||
|
||||
|
||||
import ListCustomers from './ListCustomers';
|
||||
|
||||
|
||||
const perPage = 10
|
||||
|
||||
@ -414,13 +412,13 @@ const TableSampleCustomers = ({ filterItems, setFilterItems, filters, showGrid }
|
||||
<div className="flex">
|
||||
<BaseButton
|
||||
className="my-2 mr-3"
|
||||
type='submit' color='info'
|
||||
color="success"
|
||||
label='Apply'
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<BaseButton
|
||||
className="my-2"
|
||||
type='reset' color='info' outline
|
||||
color='info'
|
||||
label='Cancel'
|
||||
onClick={handleReset}
|
||||
/>
|
||||
@ -442,21 +440,10 @@ const TableSampleCustomers = ({ filterItems, setFilterItems, filters, showGrid }
|
||||
</CardBoxModal>
|
||||
|
||||
|
||||
{customers && Array.isArray(customers) && !showGrid && (
|
||||
<ListCustomers
|
||||
customers={customers}
|
||||
loading={loading}
|
||||
onDelete={handleDeleteModalAction}
|
||||
currentPage={currentPage}
|
||||
numPages={numPages}
|
||||
onPageChange={onPageChange}
|
||||
/>
|
||||
)}
|
||||
{dataGrid}
|
||||
|
||||
|
||||
|
||||
{showGrid && dataGrid}
|
||||
|
||||
|
||||
{selectedRows.length > 0 &&
|
||||
createPortal(
|
||||
|
||||
@ -41,6 +41,112 @@ export const loadColumns = async (
|
||||
|
||||
return [
|
||||
|
||||
{
|
||||
field: 'name',
|
||||
headerName: 'CustomerName',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'email',
|
||||
headerName: 'Email',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'phone',
|
||||
headerName: 'Phone',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'company',
|
||||
headerName: 'Company',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'address',
|
||||
headerName: 'Address',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'notes',
|
||||
headerName: 'Notes',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'loyalty_points',
|
||||
headerName: 'LoyaltyPoints',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
type: 'number',
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'actions',
|
||||
type: 'actions',
|
||||
|
||||
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 CardBoxModal from '../CardBoxModal'
|
||||
import CardBox from "../CardBox";
|
||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/product_variants/product_variantsSlice'
|
||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/discounts/discountsSlice'
|
||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Field, Form, Formik } from "formik";
|
||||
@ -12,7 +12,7 @@ import {
|
||||
DataGrid,
|
||||
GridColDef,
|
||||
} from '@mui/x-data-grid';
|
||||
import {loadColumns} from "./configureProduct_variantsCols";
|
||||
import {loadColumns} from "./configureDiscountsCols";
|
||||
import _ from 'lodash';
|
||||
import dataFormatter from '../../helpers/dataFormatter'
|
||||
import {dataGridStyles} from "../../styles";
|
||||
@ -21,7 +21,7 @@ import {dataGridStyles} from "../../styles";
|
||||
|
||||
const perPage = 10
|
||||
|
||||
const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||
const TableSampleDiscounts = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
@ -40,7 +40,7 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
||||
},
|
||||
]);
|
||||
|
||||
const { product_variants, loading, count, notify: product_variantsNotify, refetch } = useAppSelector((state) => state.product_variants)
|
||||
const { discounts, loading, count, notify: discountsNotify, refetch } = useAppSelector((state) => state.discounts)
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||
@ -60,10 +60,10 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (product_variantsNotify.showNotification) {
|
||||
notify(product_variantsNotify.typeNotification, product_variantsNotify.textNotification);
|
||||
if (discountsNotify.showNotification) {
|
||||
notify(discountsNotify.typeNotification, discountsNotify.textNotification);
|
||||
}
|
||||
}, [product_variantsNotify.showNotification]);
|
||||
}, [discountsNotify.showNotification]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentUser) return;
|
||||
@ -177,7 +177,7 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
||||
|
||||
loadColumns(
|
||||
handleDeleteModalAction,
|
||||
`product_variants`,
|
||||
`discounts`,
|
||||
currentUser,
|
||||
).then((newCols) => setColumns(newCols));
|
||||
}, [currentUser]);
|
||||
@ -215,7 +215,7 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
||||
sx={dataGridStyles}
|
||||
className={'datagrid--table'}
|
||||
getRowClassName={() => `datagrid--row`}
|
||||
rows={product_variants ?? []}
|
||||
rows={discounts ?? []}
|
||||
columns={columns}
|
||||
initialState={{
|
||||
pagination: {
|
||||
@ -412,13 +412,13 @@ const TableSampleProduct_variants = ({ filterItems, setFilterItems, filters, sho
|
||||
<div className="flex">
|
||||
<BaseButton
|
||||
className="my-2 mr-3"
|
||||
type='submit' color='info'
|
||||
color="success"
|
||||
label='Apply'
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<BaseButton
|
||||
className="my-2"
|
||||
type='reset' color='info' outline
|
||||
color='info'
|
||||
label='Cancel'
|
||||
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}`,
|
||||
props.hasTextareaHeight ? 'h-24' : 'h-12',
|
||||
props.isBorderless ? 'border-0' : 'border',
|
||||
props.isTransparent ? 'bg-transparent' : `${props.websiteBg ? ` bg-skyBlueTheme-websiteBG ` : bgColor} dark:bg-dark-800`,
|
||||
props.isTransparent ? 'bg-transparent' : `${props.websiteBg ? ` bg-white` : bgColor} dark:bg-dark-800`,
|
||||
props.disabled ? 'bg-gray-200 text-gray-100 dark:bg-dark-900 disabled' : '',
|
||||
props.borderButtom ? `border-0 border-b ${props.diversity ? "placeholder-primaryText border-primaryText" : " placeholder-white border-white "} rounded-none focus:ring-0` : '',
|
||||
props.borderButtom ? `border-0 border-b ${props.diversity ? "border-gray-400" : " placeholder-white border-gray-300/10 border-white "} rounded-none focus:ring-0` : '',
|
||||
].join(' ');
|
||||
|
||||
return (
|
||||
|
||||
@ -77,7 +77,7 @@ const FormImagePicker = ({ label, icon, accept, color, isRoundIcon, path, schema
|
||||
/>
|
||||
</label>
|
||||
{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'>
|
||||
{file.name}
|
||||
</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}`}
|
||||
/>
|
||||
) : (
|
||||
<div className={'flex h-full dark:bg-dark-900/70'}>
|
||||
<div className={'flex h-full bg-slate-100 dark:bg-dark-900/70'}>
|
||||
<BaseIcon
|
||||
className='text-black dark:text-white'
|
||||
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
|
||||
ref={drag}
|
||||
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'}>
|
||||
|
||||
@ -188,7 +188,7 @@ const KanbanColumn = ({
|
||||
</div>
|
||||
))}
|
||||
{!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>
|
||||
</CardBox>
|
||||
|
||||
@ -53,7 +53,7 @@ const ListActionsPopover = ({
|
||||
size={'small'}
|
||||
>
|
||||
<BaseIcon
|
||||
className={`text-primaryText dark:text-white ${iconClassName}`}
|
||||
className={`text-black dark:text-white ${iconClassName}`}
|
||||
w='w-10'
|
||||
h='h-10'
|
||||
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'
|
||||
></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>
|
||||
|
||||
@ -37,7 +37,7 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
||||
<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`}
|
||||
>
|
||||
<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-none items-stretch flex h-14 lg:hidden">
|
||||
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
||||
|
||||
@ -106,7 +106,7 @@ export default function NavBarItem({ item }: Props) {
|
||||
<div
|
||||
className={`${
|
||||
!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]}>
|
||||
<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`}>
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
@ -76,6 +76,78 @@ const CardOrder_items = ({
|
||||
</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'>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>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@ -37,7 +37,7 @@ const ListOrder_items = ({ order_items, loading, onDelete, currentPage, numPages
|
||||
{!loading && order_items.map((item) => (
|
||||
<div key={item.id}>
|
||||
<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
|
||||
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>
|
||||
<ListActionsPopover
|
||||
onDelete={onDelete}
|
||||
|
||||
@ -412,13 +412,13 @@ const TableSampleOrder_items = ({ filterItems, setFilterItems, filters, showGrid
|
||||
<div className="flex">
|
||||
<BaseButton
|
||||
className="my-2 mr-3"
|
||||
type='submit' color='info'
|
||||
color="success"
|
||||
label='Apply'
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<BaseButton
|
||||
className="my-2"
|
||||
type='reset' color='info' outline
|
||||
color='info'
|
||||
label='Cancel'
|
||||
onClick={handleReset}
|
||||
/>
|
||||
|
||||
@ -41,6 +41,106 @@ export const loadColumns = async (
|
||||
|
||||
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',
|
||||
type: 'actions',
|
||||
|
||||
@ -76,6 +76,186 @@ const CardOrders = ({
|
||||
</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'>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>
|
||||
</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