Skip to content

Commit c19bb09

Browse files
committed
fixed reimbursement request bugs
1 parent 465cc42 commit c19bb09

4 files changed

Lines changed: 176 additions & 6 deletions

File tree

src/backend/src/utils/reimbursement-requests.utils.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,14 +160,27 @@ export const updateReimbursementProducts = async (
160160
* @param products the products to update
161161
*/
162162
const updateExistingProducts = async (products: ReimbursementProductCreateArgs[]) => {
163-
//updates the cost and name of the remaining products, which should be products that existed before that were not deleted
163+
//updates the cost, name, and refund sources of the remaining products, which should be products that existed before that were not deleted
164164
// Does not update wbs element id because we are requiring the user on the frontend to delete it from the wbs number and then adding it to another one
165165
for (const product of products) {
166+
// Delete old refund sources for this product
167+
await prisma.refund_Source.deleteMany({
168+
where: { reimbursementProductId: product.id }
169+
});
170+
171+
const refundSources = product.refundSources.map((rs) => ({
172+
indexCode: { connect: { indexCodeId: rs.indexCode.indexCodeId } },
173+
amount: rs.amount
174+
}));
175+
166176
await prisma.reimbursement_Product.update({
167177
where: { reimbursementProductId: product.id },
168178
data: {
169179
name: product.name,
170-
cost: product.cost
180+
cost: product.cost,
181+
refundSources: {
182+
create: refundSources
183+
}
171184
}
172185
});
173186
}

src/backend/tests/unmocked/reimbursement-requests.test.ts

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,4 +1125,153 @@ describe('Reimbursement Requests', () => {
11251125
expect(updatedVendor.taxExempt).toBe(true);
11261126
});
11271127
});
1128+
1129+
describe('Editing a reimbursement request', () => {
1130+
test('editing preserves refund sources on existing products', async () => {
1131+
// Get the original product info
1132+
const [originalProduct] = reimbursementRequest.reimbursementProducts;
1133+
1134+
// Edit the request, updating the product name but keeping the same refund source
1135+
await ReimbursementRequestService.editReimbursementRequest(
1136+
reimbursementRequest.reimbursementRequestId,
1137+
createdVendor.vendorId,
1138+
createdIndexCode.indexCodeId,
1139+
createdAccountCode.accountCodeId,
1140+
reimbursementRequest.totalCost,
1141+
[],
1142+
[
1143+
{
1144+
id: originalProduct.reimbursementProductId,
1145+
name: 'UPDATED GLUE',
1146+
reason: {
1147+
carNumber: 0,
1148+
projectNumber: 0,
1149+
workPackageNumber: 0
1150+
},
1151+
cost: 200000,
1152+
refundSources: [
1153+
{
1154+
indexCode: createdIndexCode,
1155+
amount: 200
1156+
}
1157+
]
1158+
}
1159+
],
1160+
[],
1161+
createdUser,
1162+
org,
1163+
new Date()
1164+
);
1165+
1166+
// Fetch the updated request and verify refund sources are preserved
1167+
const updatedRR = await ReimbursementRequestService.getSingleReimbursementRequest(
1168+
createdUser,
1169+
reimbursementRequest.reimbursementRequestId,
1170+
org
1171+
);
1172+
1173+
expect(updatedRR.reimbursementProducts).toHaveLength(1);
1174+
expect(updatedRR.reimbursementProducts[0].name).toEqual('UPDATED GLUE');
1175+
expect(updatedRR.reimbursementProducts[0].cost).toEqual(200000);
1176+
expect(updatedRR.reimbursementProducts[0].refundSources).toHaveLength(1);
1177+
expect(updatedRR.reimbursementProducts[0].refundSources[0].amount).toEqual(200);
1178+
expect(updatedRR.reimbursementProducts[0].refundSources[0].indexCode.indexCodeId).toEqual(
1179+
createdIndexCode.indexCodeId
1180+
);
1181+
});
1182+
1183+
test('editing updates refund source amounts on existing products', async () => {
1184+
const [originalProduct] = reimbursementRequest.reimbursementProducts;
1185+
1186+
// Edit with a different refund source amount
1187+
await ReimbursementRequestService.editReimbursementRequest(
1188+
reimbursementRequest.reimbursementRequestId,
1189+
createdVendor.vendorId,
1190+
createdIndexCode.indexCodeId,
1191+
createdAccountCode.accountCodeId,
1192+
reimbursementRequest.totalCost,
1193+
[],
1194+
[
1195+
{
1196+
id: originalProduct.reimbursementProductId,
1197+
name: 'GLUE',
1198+
reason: {
1199+
carNumber: 0,
1200+
projectNumber: 0,
1201+
workPackageNumber: 0
1202+
},
1203+
cost: 300000,
1204+
refundSources: [
1205+
{
1206+
indexCode: createdIndexCode,
1207+
amount: 300
1208+
}
1209+
]
1210+
}
1211+
],
1212+
[],
1213+
createdUser,
1214+
org,
1215+
new Date()
1216+
);
1217+
1218+
const updatedRR = await ReimbursementRequestService.getSingleReimbursementRequest(
1219+
createdUser,
1220+
reimbursementRequest.reimbursementRequestId,
1221+
org
1222+
);
1223+
1224+
expect(updatedRR.reimbursementProducts).toHaveLength(1);
1225+
expect(updatedRR.reimbursementProducts[0].cost).toEqual(300000);
1226+
expect(updatedRR.reimbursementProducts[0].refundSources).toHaveLength(1);
1227+
expect(updatedRR.reimbursementProducts[0].refundSources[0].amount).toEqual(300);
1228+
});
1229+
1230+
test('editing with new products (no id) creates them with refund sources', async () => {
1231+
// Edit the request, replacing the old product with a new one (no id)
1232+
await ReimbursementRequestService.editReimbursementRequest(
1233+
reimbursementRequest.reimbursementRequestId,
1234+
createdVendor.vendorId,
1235+
createdIndexCode.indexCodeId,
1236+
createdAccountCode.accountCodeId,
1237+
reimbursementRequest.totalCost,
1238+
[],
1239+
[
1240+
{
1241+
name: 'NEW TAPE',
1242+
reason: {
1243+
carNumber: 0,
1244+
projectNumber: 0,
1245+
workPackageNumber: 0
1246+
},
1247+
cost: 500,
1248+
refundSources: [
1249+
{
1250+
indexCode: createdIndexCode,
1251+
amount: 500
1252+
}
1253+
]
1254+
}
1255+
],
1256+
[],
1257+
createdUser,
1258+
org,
1259+
new Date()
1260+
);
1261+
1262+
const updatedRR = await ReimbursementRequestService.getSingleReimbursementRequest(
1263+
createdUser,
1264+
reimbursementRequest.reimbursementRequestId,
1265+
org
1266+
);
1267+
1268+
// Old product should be soft-deleted, new one created
1269+
const activeProducts = updatedRR.reimbursementProducts;
1270+
expect(activeProducts).toHaveLength(1);
1271+
expect(activeProducts[0].name).toEqual('NEW TAPE');
1272+
expect(activeProducts[0].cost).toEqual(500);
1273+
expect(activeProducts[0].refundSources).toHaveLength(1);
1274+
expect(activeProducts[0].refundSources[0].amount).toEqual(500);
1275+
});
1276+
});
11281277
});

src/frontend/src/pages/FinancePage/EditReimbursementRequest/EditReimbursementRequestRenderedDefaultValues.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const EditReimbursementRequestRenderedDefaultValues: React.FC<{
4343
dateOfExpense: reimbursementRequest.dateOfExpense ? new Date(reimbursementRequest.dateOfExpense) : undefined,
4444
accountCodeId: reimbursementRequest.accountCode.accountCodeId,
4545
reimbursementProducts: reimbursementRequest.reimbursementProducts.map((product) => ({
46+
id: product.reimbursementProductId,
4647
reason: (product.reimbursementProductReason as WBSElementData).wbsNum
4748
? (product.reimbursementProductReason as WBSElementData).wbsNum
4849
: (product.reimbursementProductReason as OtherProductReason),

src/frontend/src/pages/FinancePage/ReimbursementRequestForm/ReimbursementFormView.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,17 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
121121
const { mutateAsync: createVendor } = useCreateVendor();
122122
const user = useCurrentUser();
123123

124-
// to grab all the proper refund sources
125-
const refundSources: CreateRefundSourceArgs[] = Array.from(
126-
new Set(reimbursementProducts.flatMap((product) => product.refundSources).filter((source) => source.amount > 0))
127-
);
124+
// to grab all the proper refund sources, deduplicated by indexCodeId
125+
const refundSources: CreateRefundSourceArgs[] = (() => {
126+
const allSources = reimbursementProducts.flatMap((product) => product.refundSources).filter((source) => source.amount > 0);
127+
const seen = new Set<string>();
128+
return allSources.filter((source) => {
129+
const id = source.indexCode.indexCodeId;
130+
if (seen.has(id)) return false;
131+
seen.add(id);
132+
return true;
133+
});
134+
})();
128135

129136
const [hasConfirmedFinance, setHasConfirmedFinance] = useState(refundSources.length > 1);
130137
const toast = useToast();

0 commit comments

Comments
 (0)