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
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ interface ReimbursementRequestFormViewProps {
isLeadershipApproved?: boolean;
onSubmitToFinance?: (data: ReimbursementRequestFormInput) => void;
isSubmitting?: boolean;
applySplitShippingToProducts: (totalShipping?: number) => void;
applyProportionalShippingToProducts: (totalShipping?: number) => void;
}

const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps> = ({
Expand All @@ -112,7 +114,9 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
isEditing = false,
isLeadershipApproved = false,
onSubmitToFinance,
isSubmitting = false
isSubmitting = false,
applySplitShippingToProducts,
applyProportionalShippingToProducts
}) => {
const [datePickerOpen, setDatePickerOpen] = useState(false);
const [showAddRefundSourceModal, setShowAddRefundSourceModal] = useState(false);
Expand Down Expand Up @@ -140,14 +144,26 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
const theme = useTheme();
const products = watch('reimbursementProducts') as ReimbursementProductFormArgs[];
const accountCodeId = watch('accountCodeId');
const splitShippingValue = watch('splitShipping');

const selectedAccountCode = allAccountCodes.find((accountCode) => accountCode.accountCodeId === accountCodeId);
const indexCodes: IndexCode[] = useMemo(() => selectedAccountCode?.indexCodes ?? [], [selectedAccountCode?.indexCodes]);
const indexCodes: IndexCode[] = useMemo(() => selectedAccountCode?.indexCodes ?? [], [selectedAccountCode]);

const firstRefundSourceId = watch('indexCodeId');
const secondRefundSourceId = watch('secondaryAccount');
const hasPreFilledData = useRef(true);

const shippableProducts = products?.filter((product) => !!product.materialId) ?? [];

const allShippableProductsHaveCosts =
shippableProducts.length > 0 &&
shippableProducts.every((product) => {
const baseCost = Number((product as any).__baseCost ?? product.cost ?? 0);
return baseCost > 0;
});

const canApplyProportionalSplit = Number(splitShippingValue) > 0 && allShippableProductsHaveCosts;

useEffect(() => {
if (!hasPreFilledData.current) return;

Expand Down Expand Up @@ -233,7 +249,7 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>

const remainingRefundSources = indexCodes.filter((code) => code.indexCodeId !== firstRefundSourceId);
const calculatedTotalCost = products
.reduce((acc: number, product: ReimbursementProductFormArgs) => acc + Number(product.cost), 0)
.reduce((acc: number, product: ReimbursementProductFormArgs) => acc + Number(product.cost || 0), 0)
.toFixed(2);

const { isLoading, isError, error, data: financeDelegates } = useGetFinanceDelegates();
Expand Down Expand Up @@ -888,6 +904,71 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
)}
/>
</FormControl>
{/* Total Shipping */}
<FormControl sx={{ borderRadius: '25px', width: '100%' }}>
<FormLabel
sx={{
color: '#dd524c',
textShadow: '1.5px 0 #dd524c',
letterSpacing: '0.5px',
textDecoration: 'underline',
textUnderlineOffset: '3.5px',
textDecorationThickness: '0.6px',
paddingBottom: '2px',
fontSize: 'x-large',
fontWeight: 'bold'
}}
>
Total Shipping
</FormLabel>

<Controller
name="splitShipping"
control={control}
render={({ field: { onChange, value } }) => (
<>
<TextField
value={value ?? ''}
onChange={(e) => {
onChange(e);
}}
onBlur={() => applySplitShippingToProducts(Number(value))}
placeholder="Enter total shipping cost"
type="number"
inputProps={{ min: 0, step: 0.01 }}
size="small"
fullWidth
sx={{
'& input::-webkit-outer-spin-button, & input::-webkit-inner-spin-button': {
WebkitAppearance: 'none',
margin: 0
},
'& input[type=number]': {
MozAppearance: 'textfield'
}
}}
/>

<Button
variant="outlined"
size="small"
sx={{
mt: 1,
alignSelf: 'flex-start',
width: 'fit-content',
textTransform: 'none'
}}
disabled={!canApplyProportionalSplit}
onClick={() => applyProportionalShippingToProducts(Number(value))}
>
Split proportional to cost
</Button>
</>
)}
/>

<FormHelperText error>{errors.splitShipping?.message}</FormHelperText>
</FormControl>
</Stack>
</Grid>
</Grid>
Expand All @@ -913,6 +994,7 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
firstRefundSourceName={firstRefundSource.name}
secondRefundSourceName={secondRefundSource.name}
allProjects={allProjects}
applySplitShippingToProducts={applySplitShippingToProducts}
/>
<FormHelperText error>{errors.reimbursementProducts?.message}</FormHelperText>
</FormControl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ interface ReimbursementProductTableProps {
firstRefundSourceName?: string;
secondRefundSourceName?: string;
allProjects: ProjectPreview[];
applySplitShippingToProducts: (totalShipping?: number) => void;
}

const ListItem = styled('li')(({ theme }) => ({
Expand Down Expand Up @@ -156,7 +157,8 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
firstRefundSourceName,
secondRefundSourceName,
watch,
allProjects
allProjects,
applySplitShippingToProducts
}) => {
const uniqueWbsElementsWithProducts = new Map<
string,
Expand All @@ -175,12 +177,19 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
const [pendingMaterialIndices, setPendingMaterialIndices] = useState<Set<number>>(new Set());

const onCostBlurHandler = (value: number, index: number) => {
setValue(`reimbursementProducts.${index}.cost`, parseFloat(value.toFixed(2)));
const roundedValue = parseFloat((value || 0).toFixed(2));
const shippingCost = Number((watch(`reimbursementProducts.${index}` as const) as any)?.__shippingCost ?? 0);
const totalRowCost = roundedValue + shippingCost;

setValue(`reimbursementProducts.${index}.cost`, roundedValue);

if (firstRefundSourceIndexCode) {
setValue(`reimbursementProducts.${index}.refundSources`, [{ indexCode: firstRefundSourceIndexCode, amount: value }]);
setValue(`reimbursementProducts.${index}.refundSources`, [
{ indexCode: firstRefundSourceIndexCode, amount: totalRowCost }
]);
}
};
const totalShipping = watch('splitShipping');

const userTheme = useTheme();
const hoverColor = userTheme.palette.action.hover;
Expand Down Expand Up @@ -215,6 +224,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
if (hasMultipleRefundSources) {
const firstSourceAmount = Number(watch(`reimbursementProducts.${index}.refundSources.${0}.amount`)) || 0;
const secondSourceAmount = Number(watch(`reimbursementProducts.${index}.refundSources.${1}.amount`)) || 0;
const shippingCost = Number((watch(`reimbursementProducts.${index}` as const) as any)?.__shippingCost ?? 0);

if (firstRefundSourceIndexCode !== undefined) {
setValue(`reimbursementProducts.${index}.refundSources.${0}.indexCode`, firstRefundSourceIndexCode);
Expand All @@ -224,7 +234,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
setValue(`reimbursementProducts.${index}.refundSources.${1}.indexCode`, secondRefundSourceIndexCode);
}

setValue(`reimbursementProducts.${index}.cost`, firstSourceAmount + secondSourceAmount);
setValue(`reimbursementProducts.${index}.cost`, firstSourceAmount + secondSourceAmount - shippingCost);
}
};

Expand Down Expand Up @@ -409,6 +419,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
cost: 0,
refundSources: []
});
setTimeout(() => applySplitShippingToProducts(Number(totalShipping)), 0);
}
}}
value={null}
Expand All @@ -434,6 +445,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
cost: 0,
refundSources: []
});
setTimeout(() => applySplitShippingToProducts(Number(totalShipping)), 0);
}
}}
value={null}
Expand Down Expand Up @@ -657,18 +669,40 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
<Controller
name={`reimbursementProducts.${product.index}.cost`}
control={control}
render={({ field }) => (
<TextField
{...field}
variant="outlined"
value={field.value === 0 ? '' : field.value}
placeholder={'$ Cost'}
type="number"
fullWidth
onBlur={(e) => onCostBlurHandler(parseFloat(e.target.value), product.index)}
error={!!errors.reimbursementProducts?.[product.index]?.cost}
/>
)}
render={({ field }) => {
const shippingCost = Number(
(watch(`reimbursementProducts.${product.index}` as const) as any)
?.__shippingCost ?? 0
);
const rowTotal = Number(field.value || 0) + shippingCost;

return (
<>
<TextField
{...field}
variant="outlined"
value={field.value === 0 ? '' : field.value}
placeholder={'$ Cost'}
type="number"
fullWidth
onBlur={(e) => onCostBlurHandler(parseFloat(e.target.value), product.index)}
error={!!errors.reimbursementProducts?.[product.index]?.cost}
/>
{shippingCost > 0 && (
<TextField
value={shippingCost.toFixed(2)}
variant="outlined"
size="small"
fullWidth
disabled
margin="dense"
label="Shipping"
helperText={`Shipping + $${shippingCost.toFixed(2)} · Row total $${rowTotal.toFixed(2)}`}
/>
)}
</>
);
}}
/>
<FormHelperText error>
{errors.reimbursementProducts?.[product.index]?.cost?.message}
Expand Down Expand Up @@ -786,6 +820,26 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
</FormControl>
</Box>
)}

{Number(
(watch(`reimbursementProducts.${product.index}` as const) as any)?.__shippingCost ?? 0
) > 0 && (
<Box sx={{ width: '100%', mt: 1 }}>
<TextField
value={Number(
(watch(`reimbursementProducts.${product.index}` as const) as any)
?.__shippingCost ?? 0
).toFixed(2)}
variant="outlined"
size="small"
fullWidth
disabled
label="Shipping"
helperText={`Shipping + $${Number((watch(`reimbursementProducts.${product.index}` as const) as any)?.__shippingCost ?? 0).toFixed(2)} · Row total $${Number(watch(`reimbursementProducts.${product.index}.cost`) || 0).toFixed(2)}`}
/>
</Box>
)}

<Box
sx={{
display: { xs: 'block', md: 'none' },
Expand All @@ -805,7 +859,10 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
backgroundColor: hoverColor
}
}}
onClick={() => removeProduct(product.index)}
onClick={() => {
removeProduct(product.index);
setTimeout(() => applySplitShippingToProducts(Number(totalShipping)), 0);
}}
>
<RemoveCircleOutline />
</IconButton>
Expand Down Expand Up @@ -840,6 +897,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
cost: 0,
refundSources: []
});
setTimeout(() => applySplitShippingToProducts(Number(totalShipping)), 0);
}
e.currentTarget.blur();
}}
Expand Down
Loading