Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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,7 @@ interface ReimbursementRequestFormViewProps {
isLeadershipApproved?: boolean;
onSubmitToFinance?: (data: ReimbursementRequestFormInput) => void;
isSubmitting?: boolean;
applySplitShippingToProducts: (totalShipping?: number) => void;
}

const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps> = ({
Expand All @@ -112,7 +113,8 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
isEditing = false,
isLeadershipApproved = false,
onSubmitToFinance,
isSubmitting = false
isSubmitting = false,
applySplitShippingToProducts
}) => {
const [datePickerOpen, setDatePickerOpen] = useState(false);
const [showAddRefundSourceModal, setShowAddRefundSourceModal] = useState(false);
Expand Down Expand Up @@ -682,6 +684,55 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
<FormHelperText error>{errors.accountCodeId?.message}</FormHelperText>
</FormControl>

{/* Total Shipping */}
Comment thread
Steph375 marked this conversation as resolved.
Outdated
<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'
}
}}
/>
)}
/>

<FormHelperText error>{errors.splitShipping?.message}</FormHelperText>
</FormControl>

{/* Upload Receipts */}
<FormControl sx={{ display: 'flex', borderRadius: '25px', width: '100%' }}>
<FormLabel
Expand Down Expand Up @@ -913,6 +964,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 @@ -181,6 +183,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
setValue(`reimbursementProducts.${index}.refundSources`, [{ indexCode: firstRefundSourceIndexCode, amount: value }]);
}
};
const totalShipping = watch('splitShipping');

const userTheme = useTheme();
const hoverColor = userTheme.palette.action.hover;
Expand Down Expand Up @@ -409,6 +412,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
cost: 0,
refundSources: []
});
setTimeout(() => applySplitShippingToProducts(Number(totalShipping)), 0);
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.

use a callback instead of setTimeout

}
}}
value={null}
Expand All @@ -434,6 +438,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
cost: 0,
refundSources: []
});
setTimeout(() => applySplitShippingToProducts(Number(totalShipping)), 0);
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.

here too

}
}}
value={null}
Expand Down Expand Up @@ -805,7 +810,10 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
backgroundColor: hoverColor
}
}}
onClick={() => removeProduct(product.index)}
onClick={() => {
removeProduct(product.index);
setTimeout(() => applySplitShippingToProducts(Number(totalShipping)), 0);
Comment thread
Steph375 marked this conversation as resolved.
Outdated
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.

here too

}}
>
<RemoveCircleOutline />
</IconButton>
Expand Down Expand Up @@ -840,6 +848,7 @@ const ReimbursementProductTable: React.FC<ReimbursementProductTableProps> = ({
cost: 0,
refundSources: []
});
setTimeout(() => applySplitShippingToProducts(Number(totalShipping)), 0);
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.

here too

}
e.currentTarget.blur();
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface ReimbursementRequestInformation {
}
export interface ReimbursementRequestFormInput extends ReimbursementRequestInformation {
reimbursementProducts: ReimbursementProductFormArgs[];
splitShipping?: number;
}

export interface ReimbursementRequestDataSubmission extends ReimbursementRequestInformation {
Expand All @@ -57,6 +58,13 @@ const RECEIPTS_REQUIRED = import.meta.env.VITE_RR_RECEIPT_REQUIREMENT || 'disabl

const schema = yup.object().shape({
vendorId: yup.string().required('Vendor is required'),
splitShipping: yup
.number()
.transform((value, originalValue) => {
return originalValue === '' || originalValue === undefined ? undefined : value;
})
.optional()
.min(0.01, 'Split shipping must be greater than 0'),
indexCodeId: yup.string().required('Refund source is required'),
secondaryAccount: yup.string().test('required-if-split', 'Second refund source is required', function (value) {
if (!this.parent.$hasConfirmedFinance) return true;
Expand Down Expand Up @@ -163,7 +171,8 @@ const ReimbursementRequestForm: React.FC<ReimbursementRequestFormProps> = ({
accountCodeId: defaultValues?.accountCodeId ?? '',
description: defaultValues?.description?.trim() || '',
reimbursementProducts: defaultValues?.reimbursementProducts ?? ([] as ReimbursementProductFormArgs[]),
receiptFiles: defaultValues?.receiptFiles ?? ([] as ReimbursementReceiptUploadArgs[])
receiptFiles: defaultValues?.receiptFiles ?? ([] as ReimbursementReceiptUploadArgs[]),
splitShipping: defaultValues?.splitShipping ?? undefined
}
});

Expand All @@ -181,12 +190,45 @@ const ReimbursementRequestForm: React.FC<ReimbursementRequestFormProps> = ({
const {
fields: reimbursementProducts,
prepend: reimbursementProductPrepend,
remove: reimbursementProductRemove
remove: reimbursementProductRemove,
replace: reimbursementProductReplace
} = useFieldArray({
control,
name: 'reimbursementProducts'
});

const applySplitShippingToProducts = (totalShipping?: number) => {
const currentProducts = watch('reimbursementProducts') ?? [];

const nonShippingProducts = currentProducts.filter((product) => product.name !== 'Split Shipping');

if (!totalShipping || totalShipping <= 0 || nonShippingProducts.length === 0) {
reimbursementProductReplace(nonShippingProducts);
return;
}

const totalShippingCents = Math.round(totalShipping * 100);
const baseShippingCents = Math.floor(totalShippingCents / nonShippingProducts.length);
const remainderCents = totalShippingCents % nonShippingProducts.length;

const updatedProducts: ReimbursementProductFormArgs[] = [];

nonShippingProducts.forEach((product, index) => {
updatedProducts.push(product);

const shippingCents = baseShippingCents + (index < remainderCents ? 1 : 0);

updatedProducts.push({
Comment thread
Steph375 marked this conversation as resolved.
Outdated
name: 'Split Shipping',
reason: product.reason,
cost: shippingCents / 100,
refundSources: []
});
});

reimbursementProductReplace(updatedProducts);
};

const {
isLoading: allVendorsIsLoading,
isError: allVendorsIsError,
Expand Down Expand Up @@ -230,13 +272,11 @@ const ReimbursementRequestForm: React.FC<ReimbursementRequestFormProps> = ({
checkSecureSettingsIsLoading
)
return <LoadingIndicator />;

const onSubmitWrapper = async (data: ReimbursementRequestFormInput) => {
try {
//total cost, firstSourceAmount and secondSourceAmount is tracked in cents
const totalCost = Math.round(data.reimbursementProducts.reduce((acc, curr) => acc + curr.cost, 0) * 100);
// For each product, if multiple refund sources are enabled, the `cost` field represents
// the total amount from the first refund source amount (firstSourceAmount) and second refund source (secondSourceAmount) of that product.
// If only one refund source is present, the `cost` reflects the refund source amount for that product, and firstSourceAmount and secondSourceAmount are left as 0 since they will not needed for this scenario.

const reimbursementProducts = data.reimbursementProducts.map((product: ReimbursementProductFormArgs) => {
const anyNonZero = product.refundSources.some((rs) => Number(rs.amount) > 0);
Expand Down Expand Up @@ -371,6 +411,7 @@ const ReimbursementRequestForm: React.FC<ReimbursementRequestFormProps> = ({
isLeadershipApproved={isLeadershipApproved}
onSubmitToFinance={onSubmitToFinanceWrapper}
isSubmitting={isSubmitting}
applySplitShippingToProducts={applySplitShippingToProducts}
/>
);
};
Expand Down
Loading