Clean up the shipment table

This commit is contained in:
Flatlogic Bot 2025-05-05 06:05:51 +00:00
parent b48264df80
commit 3c4a72358d
20 changed files with 301 additions and 281 deletions

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
node_modules/
*/node_modules/
*/build/
**/node_modules/
**/build/
.DS_Store
.env

View File

@ -24,6 +24,8 @@ module.exports = class ShipmentsDBApi {
state: data.state || null,
customerCharge: data.customerCharge || null,
actualCharge: data.actualCharge || null,
businessname: data.businessname || null,
business: data.business || null,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
@ -59,6 +61,8 @@ module.exports = class ShipmentsDBApi {
state: item.state || null,
customerCharge: item.customerCharge || null,
actualCharge: item.actualCharge || null,
businessname: item.businessname || null,
business: item.business || null,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
@ -104,6 +108,11 @@ module.exports = class ShipmentsDBApi {
if (data.actualCharge !== undefined)
updatePayload.actualCharge = data.actualCharge;
if (data.businessname !== undefined)
updatePayload.businessname = data.businessname;
if (data.business !== undefined) updatePayload.business = data.business;
updatePayload.updatedById = currentUser.id;
await shipments.update(updatePayload, { transaction });
@ -299,6 +308,24 @@ module.exports = class ShipmentsDBApi {
};
}
if (filter.businessname) {
where = {
...where,
[Op.and]: Utils.ilike(
'shipments',
'businessname',
filter.businessname,
),
};
}
if (filter.business) {
where = {
...where,
[Op.and]: Utils.ilike('shipments', 'business', filter.business),
};
}
if (filter.customerChargeRange) {
const [start, end] = filter.customerChargeRange;

View File

@ -0,0 +1,49 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn(
'shipments',
'business',
{
type: Sequelize.DataTypes.TEXT,
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.removeColumn('shipments', 'business', {
transaction,
});
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -50,6 +50,14 @@ module.exports = function (sequelize, DataTypes) {
type: DataTypes.DECIMAL,
},
businessname: {
type: DataTypes.TEXT,
},
business: {
type: DataTypes.TEXT,
},
importHash: {
type: DataTypes.STRING(255),
allowNull: true,

View File

@ -9,7 +9,7 @@ const Shipments = db.shipments;
const ProductsData = [
{
productName: 'Andreas Vesalius',
productName: 'Emil Fischer',
// type code here for "images" field
@ -23,7 +23,7 @@ const ProductsData = [
},
{
productName: 'Thomas Hunt Morgan',
productName: 'Claude Levi-Strauss',
// type code here for "images" field
@ -37,7 +37,7 @@ const ProductsData = [
},
{
productName: 'Willard Libby',
productName: 'Karl Landsteiner',
// type code here for "images" field
@ -51,7 +51,7 @@ const ProductsData = [
},
{
productName: 'Theodosius Dobzhansky',
productName: 'Karl Landsteiner',
// type code here for "images" field
@ -63,59 +63,37 @@ const ProductsData = [
height: 6,
},
{
productName: 'Gertrude Belle Elion',
// type code here for "images" field
weight: 5,
length: 30,
width: 15,
height: 8,
},
];
const QuotesData = [
{
carrierName: 'Rudolf Virchow',
carrierName: 'Justus Liebig',
quotePrice: 51.15,
quotePrice: 20.13,
// type code here for "relation_one" field
},
{
carrierName: 'Tycho Brahe',
carrierName: 'Sigmund Freud',
quotePrice: 62.23,
quotePrice: 56.18,
// type code here for "relation_one" field
},
{
carrierName: 'Karl Landsteiner',
carrierName: 'Galileo Galilei',
quotePrice: 48.62,
quotePrice: 25.93,
// type code here for "relation_one" field
},
{
carrierName: 'Jean Baptiste Lamarck',
carrierName: 'Edwin Hubble',
quotePrice: 52.37,
// type code here for "relation_one" field
},
{
carrierName: 'Sheldon Glashow',
quotePrice: 43.24,
quotePrice: 48.31,
// type code here for "relation_one" field
},
@ -123,123 +101,115 @@ const QuotesData = [
const ShipmentsData = [
{
customer: 'Alfred Wegener',
customer: 'Claude Levi-Strauss',
phoneNumber: 'Charles Sherrington',
phoneNumber: 'Alfred Wegener',
address: 'Heike Kamerlingh Onnes',
address: 'Rudolf Virchow',
address2: 'Konrad Lorenz',
address2: 'Carl Gauss (Karl Friedrich Gauss)',
zipCode: 'Frederick Gowland Hopkins',
zipCode: 'Franz Boas',
city: 'Nicolaus Copernicus',
city: 'Michael Faraday',
state: 'Edward O. Wilson',
state: 'Christiaan Huygens',
// type code here for "relation_many" field
// type code here for "relation_one" field
customerCharge: 30.03,
customerCharge: 13.96,
actualCharge: 95.91,
actualCharge: 44.54,
businessname: 'Marcello Malpighi',
business: 'Max von Laue',
},
{
customer: 'Hermann von Helmholtz',
customer: 'Albert Einstein',
phoneNumber: 'Justus Liebig',
phoneNumber: 'Enrico Fermi',
address: 'Marie Curie',
address: 'Robert Koch',
address2: 'Alfred Wegener',
address2: 'James Watson',
zipCode: 'Hans Bethe',
zipCode: 'Pierre Simon de Laplace',
city: 'Enrico Fermi',
city: 'Sheldon Glashow',
state: 'Sigmund Freud',
state: 'William Bayliss',
// type code here for "relation_many" field
// type code here for "relation_one" field
customerCharge: 53.43,
customerCharge: 14.11,
actualCharge: 58.21,
actualCharge: 66.43,
businessname: 'Karl Landsteiner',
business: 'Max Delbruck',
},
{
customer: 'B. F. Skinner',
customer: 'Hans Bethe',
phoneNumber: 'Gregor Mendel',
phoneNumber: 'Sheldon Glashow',
address: 'Carl Gauss (Karl Friedrich Gauss)',
address: 'Leonard Euler',
address2: 'Alfred Wegener',
address2: 'Neils Bohr',
zipCode: 'Frederick Sanger',
zipCode: 'Johannes Kepler',
city: 'Galileo Galilei',
city: 'Max Born',
state: 'James Watson',
state: 'J. Robert Oppenheimer',
// type code here for "relation_many" field
// type code here for "relation_one" field
customerCharge: 13.46,
customerCharge: 87.48,
actualCharge: 92.16,
actualCharge: 66.23,
businessname: 'George Gaylord Simpson',
business: 'Arthur Eddington',
},
{
customer: 'Ernest Rutherford',
customer: 'Max Planck',
phoneNumber: 'Carl Gauss (Karl Friedrich Gauss)',
phoneNumber: 'Edward Teller',
address: 'John von Neumann',
address: 'Max Born',
address2: 'Jonas Salk',
address2: 'James Watson',
zipCode: 'Leonard Euler',
zipCode: 'Louis Victor de Broglie',
city: 'Claude Levi-Strauss',
city: 'Konrad Lorenz',
state: 'Pierre Simon de Laplace',
state: 'Richard Feynman',
// type code here for "relation_many" field
// type code here for "relation_one" field
customerCharge: 35.87,
customerCharge: 49.33,
actualCharge: 33.48,
},
actualCharge: 50.57,
{
customer: 'Arthur Eddington',
businessname: 'John von Neumann',
phoneNumber: 'J. Robert Oppenheimer',
address: 'Francis Crick',
address2: 'Anton van Leeuwenhoek',
zipCode: 'George Gaylord Simpson',
city: 'Claude Levi-Strauss',
state: 'Nicolaus Copernicus',
// type code here for "relation_many" field
// type code here for "relation_one" field
customerCharge: 57.76,
actualCharge: 52.63,
business: 'Max Delbruck',
},
];
@ -289,17 +259,6 @@ async function associateQuoteWithShipment() {
if (Quote3?.setShipment) {
await Quote3.setShipment(relatedShipment3);
}
const relatedShipment4 = await Shipments.findOne({
offset: Math.floor(Math.random() * (await Shipments.count())),
});
const Quote4 = await Quotes.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Quote4?.setShipment) {
await Quote4.setShipment(relatedShipment4);
}
}
// Similar logic for "relation_many"
@ -348,17 +307,6 @@ async function associateShipmentWithQuote() {
if (Shipment3?.setQuote) {
await Shipment3.setQuote(relatedQuote3);
}
const relatedQuote4 = await Quotes.findOne({
offset: Math.floor(Math.random() * (await Quotes.count())),
});
const Shipment4 = await Shipments.findOne({
order: [['id', 'ASC']],
offset: 4,
});
if (Shipment4?.setQuote) {
await Shipment4.setQuote(relatedQuote4);
}
}
module.exports = {

View File

@ -41,6 +41,12 @@ router.use(checkCrudPermissions('shipments'));
* state:
* type: string
* default: state
* businessname:
* type: string
* default: businessname
* business:
* type: string
* default: business
* customerCharge:
* type: integer
@ -338,6 +344,8 @@ router.get(
'zipCode',
'city',
'state',
'businessname',
'business',
'customerCharge',
'actualCharge',

View File

@ -61,6 +61,10 @@ module.exports = class SearchService {
'city',
'state',
'businessname',
'business',
],
};
const columnsInt = {

View File

@ -188,6 +188,28 @@ const CardShipments = ({
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Businessname
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.businessname}
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Business
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.business}
</div>
</dd>
</div>
</dl>
</li>
))}

View File

@ -119,6 +119,16 @@ const ListShipments = ({
<p className={'text-xs text-gray-500 '}>Actual Charge</p>
<p className={'line-clamp-2'}>{item.actualCharge}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Businessname</p>
<p className={'line-clamp-2'}>{item.businessname}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Business</p>
<p className={'line-clamp-2'}>{item.business}</p>
</div>
</Link>
<ListActionsPopover
onDelete={onDelete}

View File

@ -38,175 +38,77 @@ export const loadColumns = async (
const hasUpdatePermission = hasPermission(user, 'UPDATE_SHIPMENTS');
return [
{
field: 'createdAt',
headerName: 'Ship Date',
type: 'date',
flex: 1,
minWidth: 160,
valueFormatter: (params: GridValueGetterParams) => params.value ? new Date(params.value).toLocaleDateString() : '',
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
},
{
field: 'business',
headerName: 'Business',
flex: 1,
minWidth: 120,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
},
{
field: 'customer',
headerName: 'Customer',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'phoneNumber',
headerName: 'Phone Number',
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: 'address2',
headerName: 'Address 2',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'zipCode',
headerName: 'Zip Code',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'city',
headerName: 'City',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'state',
headerName: 'State',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'products',
headerName: 'Products',
field: 'zipCode',
headerName: 'Zip Code',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: false,
sortable: false,
type: 'singleSelect',
valueFormatter: ({ value }) =>
dataFormatter.productsManyListFormatter(value).join(', '),
renderEditCell: (params) => (
<DataGridMultiSelect {...params} entityName={'products'} />
),
editable: hasUpdatePermission,
},
{
field: 'quote',
headerName: 'Quote',
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('quotes'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
valueGetter: (params: GridValueGetterParams) => params.row.quote?.quotePrice ?? '',
},
{
field: 'customerCharge',
headerName: 'Customer Charge',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
type: 'number',
},
{
field: 'actualCharge',
headerName: 'Actual Charge',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
type: 'number',
},
{
field: 'actions',
type: 'actions',
minWidth: 30,
field: 'actions',
headerName: 'Actions',
minWidth: 80,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
getActions: (params: GridRowParams) => {
return [
<ListActionsPopover
onDelete={onDelete}
itemId={params?.row?.id}
pathEdit={`/shipments/shipments-edit/?id=${params?.row?.id}`}
pathView={`/shipments/shipments-view/?id=${params?.row?.id}`}
key={1}
hasUpdatePermission={hasUpdatePermission}
/>,
];
},
getActions: (params: GridRowParams) => [
<ListActionsPopover
onDelete={onDelete}
itemId={params.row.id}
pathEdit={`/shipments/shipments-edit/?id=${params.row.id}`}
pathView={`/shipments/shipments-view/?id=${params.row.id}`}
key={1}
hasUpdatePermission={hasUpdatePermission}
/>
],
},
];
};

View File

@ -7,47 +7,36 @@ const menuAside: MenuAsideItem[] = [
icon: icon.mdiViewDashboardOutline,
label: 'Dashboard',
},
{
href: '/users/users-list',
label: 'Users',
href: '/shipments/shipments-list',
label: 'Shipments',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiAccountGroup ? icon.mdiAccountGroup : icon.mdiTable,
permissions: 'READ_USERS',
icon: icon.mdiTruck || icon.mdiTable,
permissions: 'READ_SHIPMENTS',
},
{
href: '/products/products-list',
label: 'Products',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiPackageVariant ? icon.mdiPackageVariant : icon.mdiTable,
icon: icon.mdiPackageVariant || icon.mdiTable,
permissions: 'READ_PRODUCTS',
},
{
href: '/quotes/quotes-list',
label: 'Quotes',
href: '/users/users-list',
label: 'Users',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiCurrencyUsd ? icon.mdiCurrencyUsd : icon.mdiTable,
permissions: 'READ_QUOTES',
},
{
href: '/shipments/shipments-list',
label: 'Shipments',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiTruck ? icon.mdiTruck : icon.mdiTable,
permissions: 'READ_SHIPMENTS',
icon: icon.mdiAccountGroup || icon.mdiTable,
permissions: 'READ_USERS',
},
{
href: '/roles/roles-list',
label: 'Roles',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiShieldAccountVariantOutline
? icon.mdiShieldAccountVariantOutline
: icon.mdiTable,
icon: icon.mdiShieldAccountVariantOutline || icon.mdiTable,
permissions: 'READ_ROLES',
},
{
@ -55,9 +44,7 @@ const menuAside: MenuAsideItem[] = [
label: 'Permissions',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiShieldAccountOutline
? icon.mdiShieldAccountOutline
: icon.mdiTable,
icon: icon.mdiShieldAccountOutline || icon.mdiTable,
permissions: 'READ_PERMISSIONS',
},
{
@ -65,14 +52,6 @@ const menuAside: MenuAsideItem[] = [
label: 'Profile',
icon: icon.mdiAccountCircle,
},
{
href: '/api-docs',
target: '_blank',
label: 'Swagger API',
icon: icon.mdiFileCode,
permissions: 'READ_API_DOCS',
},
];
export default menuAside;

View File

@ -142,7 +142,7 @@ const Dashboard = () => {
)}
{rolesWidgets &&
rolesWidgets.map((widget) => (
rolesWidgets.filter((widget) => widget.label !== 'Product Shipment Counts').map((widget) => (
<SmartWidget
key={widget.id}
userId={currentUser?.id}

View File

@ -97,6 +97,10 @@ const QuotesView = () => {
<th>Customer Charge</th>
<th>Actual Charge</th>
<th>Businessname</th>
<th>Business</th>
</tr>
</thead>
<tbody>
@ -130,6 +134,10 @@ const QuotesView = () => {
</td>
<td data-label='actualCharge'>{item.actualCharge}</td>
<td data-label='businessname'>{item.businessname}</td>
<td data-label='business'>{item.business}</td>
</tr>
))}
</tbody>

View File

@ -57,6 +57,10 @@ const EditShipments = () => {
customerCharge: '',
actualCharge: '',
businessname: '',
business: '',
};
const [initialValues, setInitialValues] = useState(initVals);
@ -177,6 +181,14 @@ const EditShipments = () => {
/>
</FormField>
<FormField label='Businessname'>
<Field name='businessname' placeholder='Businessname' />
</FormField>
<FormField label='Business'>
<Field name='business' placeholder='Business' />
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />

View File

@ -57,6 +57,10 @@ const EditShipmentsPage = () => {
customerCharge: '',
actualCharge: '',
businessname: '',
business: '',
};
const [initialValues, setInitialValues] = useState(initVals);
@ -175,6 +179,14 @@ const EditShipmentsPage = () => {
/>
</FormField>
<FormField label='Businessname'>
<Field name='businessname' placeholder='Businessname' />
</FormField>
<FormField label='Business'>
<Field name='business' placeholder='Business' />
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />

View File

@ -36,6 +36,8 @@ const ShipmentsTablesPage = () => {
{ label: 'Zip Code', title: 'zipCode' },
{ label: 'City', title: 'city' },
{ label: 'State', title: 'state' },
{ label: 'Businessname', title: 'businessname' },
{ label: 'Business', title: 'business' },
{ label: 'Customer Charge', title: 'customerCharge', number: 'true' },
{ label: 'Actual Charge', title: 'actualCharge', number: 'true' },

View File

@ -54,6 +54,10 @@ const initialValues = {
customerCharge: '',
actualCharge: '',
businessname: '',
business: '',
};
const ShipmentsNew = () => {
@ -147,6 +151,14 @@ const ShipmentsNew = () => {
/>
</FormField>
<FormField label='Businessname'>
<Field name='businessname' placeholder='Businessname' />
</FormField>
<FormField label='Business'>
<Field name='business' placeholder='Business' />
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />

View File

@ -36,6 +36,8 @@ const ShipmentsTablesPage = () => {
{ label: 'Zip Code', title: 'zipCode' },
{ label: 'City', title: 'city' },
{ label: 'State', title: 'state' },
{ label: 'Businessname', title: 'businessname' },
{ label: 'Business', title: 'business' },
{ label: 'Customer Charge', title: 'customerCharge', number: 'true' },
{ label: 'Actual Charge', title: 'actualCharge', number: 'true' },

View File

@ -161,6 +161,16 @@ const ShipmentsView = () => {
<p>{shipments?.actualCharge || 'No data'}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Businessname</p>
<p>{shipments?.businessname}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Business</p>
<p>{shipments?.business}</p>
</div>
<>
<p className={'block font-bold mb-2'}>Quotes Shipment</p>
<CardBox

View File

@ -78,7 +78,7 @@ module.exports = {
diversityMain: '#5A7BEB',
diversityHeader: '#5A7BEB',
websiteBG: '#FFFFFF',
websiteBG: '#EDF2FA',
900: '#14142A',
800: '#DFE7F4',
},