add bulk bill creation across subsequent months
Implement ability to add newly created bills to all subsequent months with toggle option (disabled by default). Includes duplicate checking and efficient bulk operations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({
|
||||
_id: z.string(),
|
||||
billName: z.coerce.string().min(1, t("bill-name-required")),
|
||||
billNotes: z.string(),
|
||||
addToSubsequentMonths: z.boolean().optional(),
|
||||
payedAmount: z.string().nullable().transform((val, ctx) => {
|
||||
|
||||
if(!val || val === '') {
|
||||
@@ -123,6 +124,7 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
|
||||
.safeParse({
|
||||
billName: formData.get('billName'),
|
||||
billNotes: formData.get('billNotes'),
|
||||
addToSubsequentMonths: formData.get('addToSubsequentMonths') === 'on',
|
||||
payedAmount: formData.get('payedAmount'),
|
||||
});
|
||||
|
||||
@@ -138,6 +140,7 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
|
||||
const {
|
||||
billName,
|
||||
billNotes,
|
||||
addToSubsequentMonths,
|
||||
payedAmount,
|
||||
} = validatedFields.data;
|
||||
|
||||
@@ -183,25 +186,89 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
|
||||
]
|
||||
});
|
||||
} else {
|
||||
// find a location with the given locationID
|
||||
const post = await dbClient.collection<BillingLocation>("lokacije").updateOne(
|
||||
// Create new bill - add to current location first
|
||||
const newBill = {
|
||||
_id: (new ObjectId()).toHexString(),
|
||||
name: billName,
|
||||
paid: billPaid,
|
||||
attachment: billAttachment,
|
||||
notes: billNotes,
|
||||
payedAmount,
|
||||
barcodeImage,
|
||||
};
|
||||
|
||||
// Add to current location
|
||||
await dbClient.collection<BillingLocation>("lokacije").updateOne(
|
||||
{
|
||||
_id: locationId, // find a location with the given locationID
|
||||
userId // make sure that the location belongs to the user
|
||||
},
|
||||
{
|
||||
$push: {
|
||||
bills: {
|
||||
_id: (new ObjectId()).toHexString(),
|
||||
name: billName,
|
||||
paid: billPaid,
|
||||
attachment: billAttachment,
|
||||
notes: billNotes,
|
||||
payedAmount,
|
||||
barcodeImage,
|
||||
}
|
||||
bills: newBill
|
||||
}
|
||||
});
|
||||
|
||||
// If addToSubsequentMonths is enabled, add to subsequent months
|
||||
if (addToSubsequentMonths && billYear && billMonth) {
|
||||
// Get the current location to find its name
|
||||
const currentLocation = await dbClient.collection<BillingLocation>("lokacije")
|
||||
.findOne({ _id: locationId, userId }, { projection: { bills: 0 } });
|
||||
|
||||
if (currentLocation) {
|
||||
// Find all subsequent months that have the same location name
|
||||
const subsequentLocations = await dbClient.collection<BillingLocation>("lokacije")
|
||||
.find({
|
||||
userId,
|
||||
name: currentLocation.name,
|
||||
$or: [
|
||||
{ "yearMonth.year": { $gt: billYear } },
|
||||
{
|
||||
"yearMonth.year": billYear,
|
||||
"yearMonth.month": { $gt: billMonth }
|
||||
}
|
||||
]
|
||||
}, { projection: { bills: 0 } })
|
||||
.toArray();
|
||||
|
||||
// For each subsequent location, check if bill with same name already exists
|
||||
const updateOperations = [];
|
||||
for (const location of subsequentLocations) {
|
||||
const existingBill = await dbClient.collection<BillingLocation>("lokacije")
|
||||
.findOne({
|
||||
_id: location._id,
|
||||
"bills.name": billName
|
||||
}, { projection: { "bills.$": 1 } });
|
||||
|
||||
// Only add if bill with same name doesn't already exist
|
||||
if (!existingBill) {
|
||||
updateOperations.push({
|
||||
updateOne: {
|
||||
filter: { _id: location._id, userId },
|
||||
update: {
|
||||
$push: {
|
||||
bills: {
|
||||
_id: (new ObjectId()).toHexString(),
|
||||
name: billName,
|
||||
paid: false, // New bills in subsequent months are unpaid
|
||||
attachment: null, // No attachment for subsequent months
|
||||
notes: billNotes,
|
||||
payedAmount: null,
|
||||
barcodeImage: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Execute all update operations at once if any
|
||||
if (updateOperations.length > 0) {
|
||||
await dbClient.collection<BillingLocation>("lokacije").bulkWrite(updateOperations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(billYear && billMonth ) {
|
||||
await gotoHome({ year: billYear, month: billMonth });
|
||||
|
||||
@@ -202,6 +202,16 @@ export const BillEditForm:FC<BillEditFormProps> = ({ location, bill }) => {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Show toggle only when adding a new bill (not editing) */}
|
||||
{!bill && (
|
||||
<div className="form-control mt-4">
|
||||
<label className="label cursor-pointer">
|
||||
<span className="label-text">{t("add-to-subsequent-months")}</span>
|
||||
<input type="checkbox" name="addToSubsequentMonths" className="toggle toggle-primary" />
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="pt-4">
|
||||
<button type="submit" className="btn btn-primary">{t("save-button")}</button>
|
||||
<Link className="btn btn-neutral ml-3" href={`/?year=${billYear}&month=${billMonth}`}>{t("cancel-button")}</Link>
|
||||
|
||||
Reference in New Issue
Block a user