Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/backend/src/controllers/projects.controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ export default class ProjectsController {
quantity,
unitName,
price,
subtotal,
linkUrl,
notes
} = req.body;
Expand All @@ -237,7 +236,6 @@ export default class ProjectsController {
manufacturerPartNumber,
quantity,
price,
subtotal,
notes,
assemblyId,
pdmFileName,
Expand Down Expand Up @@ -397,7 +395,6 @@ export default class ProjectsController {
quantity,
unitName,
price,
subtotal,
Comment thread
cielbellerose marked this conversation as resolved.
linkUrl,
notes
} = req.body;
Expand All @@ -413,7 +410,6 @@ export default class ProjectsController {
manufacturerPartNumber,
quantity,
price,
subtotal,
notes,
unitName,
assemblyId,
Expand Down
3 changes: 0 additions & 3 deletions src/backend/src/prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3537,7 +3537,6 @@ const performSeed: () => Promise<void> = async () => {
'abcdef',
new Decimal(20),
30,
600,
'Here are some notes',
assembly1.assemblyId,
undefined,
Expand All @@ -3560,7 +3559,6 @@ const performSeed: () => Promise<void> = async () => {
'bacfed',
new Decimal(10),
7,
70,
'Here are some more notes',
undefined,
undefined,
Expand All @@ -3583,7 +3581,6 @@ const performSeed: () => Promise<void> = async () => {
'lalsd',
new Decimal(5),
10,
50,
undefined,
undefined,
undefined
Expand Down
19 changes: 12 additions & 7 deletions src/backend/src/services/boms.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export default class BillOfMaterialsService {
* @param manufacturerPartNumber the manufacturer part number for the material (optional)
* @param quantity the quantity of material as a number (optional)
* @param price the price of the material in whole cents (optional)
* @param subtotal the subtotal of the price for the material in whole cents (optional)
* @param notes any notes about the material as a string (optional)
* @param assemblyId the id of the Assembly for the material (optional)
* @param pdmFileName the name of the pdm file for the material (optional)
Expand All @@ -75,7 +74,6 @@ export default class BillOfMaterialsService {
manufacturerPartNumber?: string,
quantity?: Decimal,
price?: number,
subtotal?: number,
notes?: string,
assemblyId?: string,
pdmFileName?: string,
Expand Down Expand Up @@ -119,6 +117,9 @@ export default class BillOfMaterialsService {

if (!perms) throw new AccessDeniedException('create materials');

const computedSubtotal =
price !== undefined && quantity !== undefined ? Math.round(price * Number(quantity)) : undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not what Wave was talking about with removing from backend everywhere

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

she mentioned not passing it to the backend and just calculating it in the service function. it is also still in MaterialFormView in the frontend but thats bc the modal shows a preview of what the subtotal will be before submitting


const createdMaterial = await prisma.material.create({
data: {
userCreatedId: creator.userId,
Expand All @@ -132,7 +133,7 @@ export default class BillOfMaterialsService {
quantity,
unitId: unit ? unit.id : null,
price,
subtotal,
subtotal: computedSubtotal,
linkUrl,
notes,
dateCreated: new Date(),
Expand Down Expand Up @@ -611,7 +612,6 @@ export default class BillOfMaterialsService {
* @param manufacturerPartNumber the manufacturerPartNumber of the edited material (optional)
* @param quantity the quantity of the edited material (optional)
* @param price the price of the edited material (optional)
* @param subtotal the subtotal of the edited material (optional)
* @param notes the notes of the edited material (optional)
* @param unitName the unit name of the edited material (optional)
* @param assemblyId the assembly id of the edited material (optional)
Expand All @@ -631,7 +631,6 @@ export default class BillOfMaterialsService {
manufacturerPartNumber?: string,
quantity?: Decimal,
price?: number,
subtotal?: number,
notes?: string,
unitName?: string,
assemblyId?: string,
Expand Down Expand Up @@ -670,6 +669,12 @@ export default class BillOfMaterialsService {
manufacturer = await BillOfMaterialsService.getSingleManufacturerWithQueryArgs(manufacturerName, organization);
}

// recalculate subtotal on edits
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're going to take subtotal out of the endpoints in the backend, we should take it out of the frontend hooks too since now it's being passed and then dropped

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still relevant I think?

const finalPrice = price ?? material.price ?? undefined;
const finalQuantity = quantity ?? material.quantity ?? undefined;
const computedSubtotal =
finalPrice !== undefined && finalQuantity !== undefined ? Math.round(finalPrice * Number(finalQuantity)) : undefined;

const updatedMaterial = await prisma.material.update({
where: { materialId },
data: {
Expand All @@ -681,12 +686,12 @@ export default class BillOfMaterialsService {
quantity,
unitId: unit ? unit.id : null,
price,
subtotal,
subtotal: computedSubtotal,
linkUrl,
notes,
wbsElementId: project.wbsElementId,
assemblyId,
pdmFileName
pdmFileName: pdmFileName !== undefined ? pdmFileName || null : undefined
},
...getMaterialQueryArgs(organization.organizationId)
});
Expand Down
1 change: 0 additions & 1 deletion src/backend/src/utils/validation.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,6 @@ export const materialValidators = [
decimalMinZero(body('quantity')).optional(),
nonEmptyString(body('unitName')).optional(),
intMinZero(body('price')).optional(), // in cents
intMinZero(body('subtotal')).optional(), // in cents
body('linkUrl').optional().isString(),
body('notes').isString().optional()
];
Expand Down
13 changes: 4 additions & 9 deletions src/backend/tests/unmocked/project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ describe('Material Tests', () => {
manufacturer.name,
'lalsd',
new Decimal(5),
10,
50
10
);

expect(material.name).toEqual('100k Resistor');
Expand Down Expand Up @@ -99,7 +98,6 @@ describe('Material Tests', () => {
'CAP-100UF',
new Decimal(10),
50,
500,
'Test notes'
);

Expand All @@ -114,8 +112,7 @@ describe('Material Tests', () => {
manufacturer.name,
'CAP-220UF',
new Decimal(5),
75,
375
75
);

const newMaterialIds = await BillOfMaterials.copyMaterialsToProject(
Expand Down Expand Up @@ -185,8 +182,7 @@ describe('Material Tests', () => {
manufacturer.name,
'lalsd',
new Decimal(5),
10,
50
10
);

const newMaterial = await BillOfMaterials.editMaterial(
Expand All @@ -200,8 +196,7 @@ describe('Material Tests', () => {
manufacturer.name,
'lalsd',
new Decimal(5),
10,
50
10
);

expect(newMaterial.name).toEqual('100k Resistor Updated');
Expand Down
6 changes: 2 additions & 4 deletions src/backend/tests/unmocked/reimbursement-requests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1170,8 +1170,7 @@ describe('Reimbursement Requests', () => {
manufacturerId: manufacturer.id,
linkUrl: 'https://example.com',
quantity: 1,
price: 100,
subtotal: 100
price: 100
}
});
});
Expand Down Expand Up @@ -1276,8 +1275,7 @@ describe('Reimbursement Requests', () => {
manufacturerId: material.manufacturerId,
linkUrl: 'https://example.com',
quantity: 2,
price: 200,
subtotal: 400
price: 200
}
});

Expand Down
21 changes: 21 additions & 0 deletions src/frontend/src/hooks/bom.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,27 @@ export const useCreateMaterialType = () => {
);
};

/**
* Custom React hook to edit a material's status inline.
* @param wbsNum The wbs element the material belongs to
* @returns mutation function to edit a material's status
*/
export const useEditMaterialById = (wbsNum: WbsNumber) => {
const queryClient = useQueryClient();
return useMutation<Material, Error, { materialId: string; payload: MaterialDataSubmission }>(
['materials', 'edit'],
async ({ materialId, payload }) => {
const data = await editMaterial(materialId, payload);
return data;
},
{
onSuccess: () => {
queryClient.invalidateQueries(['materials', wbsPipe(wbsNum)]);
}
}
);
};

export const useGetAssembliesForWbsElement = (wbsNum: WbsNumber) => {
return useQuery<Assembly[], Error>(['assemblies', wbsPipe(wbsNum)], async () => {
const { data } = await getAssembliesForWbsElement(wbsNum);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,21 @@ interface BOMTableProps {
columns: GridColumns<BomRow>;
materials: Material[];
assemblies: Assembly[];
processRowUpdate: (newRow: BomRow, oldRow: BomRow) => Promise<BomRow>;
onProcessRowUpdateError: (error: unknown) => void;
editPerms: boolean;
}

const BOMTable: React.FC<BOMTableProps> = ({ setHideColumn, assignMaterial, columns, materials, assemblies }) => {
const BOMTable: React.FC<BOMTableProps> = ({
setHideColumn,
assignMaterial,
columns,
materials,
assemblies,
processRowUpdate,
onProcessRowUpdateError,
editPerms
}) => {
const [openRows, setOpenRows] = useState<String[]>([]);
const [draggedMaterial, setDraggedMaterial] = useState<Material | null>(null);

Expand Down Expand Up @@ -58,8 +70,9 @@ const BOMTable: React.FC<BOMTableProps> = ({ setHideColumn, assignMaterial, colu
assembly.materials.reduce(addMaterialCosts, 0)
)} ${arrowSymbol(assembly.assemblyId)}`,
pdmFileName: '',
quantity: '',
price: '',
quantity: undefined,
price: undefined,
unitName: undefined,
subtotal: '',
link: '',
notes: '',
Expand Down Expand Up @@ -139,13 +152,19 @@ const BOMTable: React.FC<BOMTableProps> = ({ setHideColumn, assignMaterial, colu
rows={rows.concat(materialsWithAssemblies.filter(isAssemblyOpen))}
getRowClassName={(params) => {
const stripe = params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd';
const isAssemblyRow = String(params.row.id).startsWith('assembly-');
const isAssemblyRow = params.row.id.startsWith('assembly-');
return `super-app-theme--${stripe}${isAssemblyRow ? ' super-app-theme--assembly' : ''}`;
}}
rowsPerPageOptions={[100]}
sx={bomTableStyles.datagrid}
disableSelectionOnClick
autoHeight={false}
experimentalFeatures={{ newEditingApi: true }}
processRowUpdate={
processRowUpdate as unknown as (newRow: GridValidRowModel, oldRow: GridValidRowModel) => Promise<GridValidRowModel>
}
onProcessRowUpdateError={onProcessRowUpdateError}
isCellEditable={(params) => editPerms && params.row.id.startsWith('assembly')}
onRowClick={openAssembly}
componentsProps={{
row: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useState } from 'react';
import { Box } from '@mui/system';
import { GridRenderCellParams } from '@mui/x-data-grid';
import { MaterialStatus } from 'shared';
import { Typography } from '@mui/material';
import { Menu, MenuItem, Typography } from '@mui/material';
import { displayEnum } from '../../../../utils/pipes';

const getStatusColor = (status: MaterialStatus) => {
Expand Down Expand Up @@ -33,6 +34,76 @@ const bomStatusChipStyle = (status: MaterialStatus) => ({
textAlign: 'center'
});

interface StatusDropdownCellProps {
status: MaterialStatus;
disabled?: boolean;
onStatusChange: (newStatus: MaterialStatus) => void;
}

export const StatusDropdownCell: React.FC<StatusDropdownCellProps> = ({ status, disabled, onStatusChange }) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);

const handleClick = (event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation();
if (!disabled) setAnchorEl(event.currentTarget);
};

const handleClose = () => setAnchorEl(null);

const handleSelect = (newStatus: MaterialStatus) => {
onStatusChange(newStatus);
handleClose();
};

return (
<>
<Box
sx={{
...bomStatusChipStyle(status),
cursor: disabled ? 'default' : 'pointer',
gap: '2px'
}}
onClick={handleClick}
>
<Typography fontSize={{ xs: '11px', sm: '14px' }} color="black">
{displayEnum(status)}
</Typography>
</Box>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleClose}
slotProps={{
paper: {
sx: {
padding: 0,
background: 'transparent',
borderRadius: '6px',
overflow: 'hidden'
}
},
list: { disablePadding: true }
}}
>
{Object.values(MaterialStatus)
.filter((s) => s !== status)
.map((s) => {
const chipStyle = bomStatusChipStyle(s);
return (
<MenuItem key={s} onClick={() => handleSelect(s)} sx={{ padding: 0 }}>
<Box sx={{ ...chipStyle, borderRadius: 0, minWidth: '130px', width: '100%' }}>
<Typography fontSize="14px" color="black">
{displayEnum(s)}
</Typography>
</Box>
</MenuItem>
);
})}
</Menu>
</>
);
};

export const renderStatusBOM = (params: GridRenderCellParams) => {
if (!params.value) return;
const status = params.value as MaterialStatus;
Expand Down
Loading
Loading