diff --git a/email-worker/src/lib/emailSenders.ts b/email-worker/src/lib/emailSenders.ts index 4721b73..a4117e1 100644 --- a/email-worker/src/lib/emailSenders.ts +++ b/email-worker/src/lib/emailSenders.ts @@ -1,5 +1,5 @@ import { Db, ObjectId } from 'mongodb'; -import { BillingLocation, EmailStatus, UserSettings, generateShareId } from '@evidencija-rezija/shared-code'; +import { BillingLocation, BillsNotificationStatus, EmailStatus, RentNotificationStatus, UserSettings, generateShareId } from '@evidencija-rezija/shared-code'; import { sendEmail } from './mailgunService'; import { createLogger } from './logger'; import { loadAndRender } from './emailTemplates'; @@ -115,11 +115,11 @@ export async function sendRentDueNotifications(db: Db, budget: number): Promise< 'yearMonth.year': currentYear, 'yearMonth.month': currentMonth, 'tenantEmailStatus': EmailStatus.Verified, - 'rentDueNotificationEnabled': true, + 'rentNotificationEnabled': true, 'rentDueDay': currentDay, $or: [ - { 'rentDueNotificationStatus': { $exists: false } }, - { 'rentDueNotificationStatus': null } + { 'rentNotificationStatus': { $exists: false } }, + { 'rentNotificationStatus': null } ] }) .toArray(); @@ -170,10 +170,10 @@ export async function sendRentDueNotifications(db: Db, budget: number): Promise< }); // Update location status - const newStatus = success ? 'sent' : 'failed'; + const newStatus = success ? RentNotificationStatus.Sent : RentNotificationStatus.Failed; await db.collection('lokacije').updateOne( { _id: location._id }, - { $set: { rentDueNotificationStatus: newStatus } } + { $set: { rentNotificationStatus: newStatus } } ); if (success) { @@ -213,8 +213,8 @@ export async function sendUtilityBillsNotifications(db: Db, budget: number): Pro 'yearMonth.year': currentYear, 'yearMonth.month': currentMonth, 'tenantEmailStatus': EmailStatus.Verified, - 'billFwdEnabled': true, - 'billFwdStatus': 'pending' + 'billsNotificationEnabled': true, + 'billsNotificationStatus': BillsNotificationStatus.Scheduled }) .toArray(); @@ -263,10 +263,10 @@ export async function sendUtilityBillsNotifications(db: Db, budget: number): Pro }); // Update location status - const newStatus = success ? 'sent' : 'failed'; + const newStatus = success ? BillsNotificationStatus.Sent : BillsNotificationStatus.Failed; await db.collection('lokacije').updateOne( { _id: location._id }, - { $set: { billFwdStatus: newStatus } } + { $set: { billsNotificationStatus: newStatus } } ); if (success) { diff --git a/shared-code/src/db-types.ts b/shared-code/src/db-types.ts index 38da6bf..f3d1807 100644 --- a/shared-code/src/db-types.ts +++ b/shared-code/src/db-types.ts @@ -49,6 +49,29 @@ export enum EmailStatus { Unsubscribed = "unsubscribed" } +export enum BillsNotificationStrategy { + /** Notify tenant when bill is payed */ + WhenPayed = "when-payed", + /** Notify tenant when bill gets an attachment */ + WhenAttached = "when-attached" +} + +export enum BillsNotificationStatus { + /** Bills notification is scheduled to be sent */ + Scheduled = "scheduled", + /** Bills notification has been sent */ + Sent = "sent", + /** Sending of bills notification failed */ + Failed = "failed" +} + +export enum RentNotificationStatus { + /** notification has been sent */ + Sent = "sent", + /** Sending of notification failed */ + Failed = "failed" +} + /** bill object in the form returned by MongoDB */ export interface BillingLocation { _id: string; @@ -84,25 +107,25 @@ export interface BillingLocation { /** (optional) language for tenant notification emails */ tenantEmailLanguage?: "hr" | "en" | null; /** (optional) whether to automatically notify tenant */ - billFwdEnabled?: boolean | null; + billsNotificationEnabled?: boolean | null; /** (optional) bill forwarding strategy */ - billFwdStrategy?: "when-payed" | "when-attached" | null; + billsNotificationStrategy?: BillsNotificationStrategy | null; /** (optional) bill forwarding status */ - billFwdStatus?: "pending" | "sent" | "failed" | null; + billsNotificationStatus?: BillsNotificationStatus | null; + /** (optional) utility bills proof of payment attachment */ + billsProofOfPayment?: FileAttachment|null; /** (optional) whether to automatically send rent notification */ - rentDueNotificationEnabled?: boolean | null; + rentNotificationEnabled?: boolean | null; + /** (optional) when was the rent due notification sent */ + rentNotificationStatus?: RentNotificationStatus | null; + /** (optional) rent proof of payment attachment */ + rentProofOfPayment?: FileAttachment|null; /** (optional) day of month when rent is due (1-31) */ rentDueDay?: number | null; - /** (optional) when was the rent due notification sent */ - rentDueNotificationStatus?: "sent" | "failed" | null; /** (optional) monthly rent amount in cents */ rentAmount?: number | null; /** (optional) whether the location has been seen by tenant */ seenByTenantAt?: Date | null; - /** (optional) utility bills proof of payment attachment */ - utilBillsProofOfPayment?: FileAttachment|null; - /** (optional) rent proof of payment attachment */ - rentProofOfPayment?: FileAttachment|null; /** (optional) share link expiry timestamp */ shareTTL?: Date; /** (optional) when tenant first visited the share link */ diff --git a/sprints/email-worker.md b/sprints/email-worker.md index a350201..8617635 100644 --- a/sprints/email-worker.md +++ b/sprints/email-worker.md @@ -90,16 +90,16 @@ The process of sending rent-due e-mail notifications is as follows: - fetch all `BillingLocations` from `lokacije` collection (see `locationActions.ts` in `web-app` workspace) - filter only records which satisfy the following: - `yearMonth.year` and `yearMonth.month` equal to current year and month - `tenantEmailStatus` is equal to `EmailStatus.Verified` - - `rentDueNotificationEnabled === true` + - `rentNotificationEnabled === true` - `rentDueDay` = current day (1-31) in CET timezone - - `rentDueNotificationStatus` === undefined OR `rentDueNotificationStatus` === null + - `rentNotificationStatus` === undefined OR `rentNotificationStatus` === null - for each record found - check the e-mail budget counter -> if the value is 0 then exit - compile an e-mail containing the content listed below - send the e-mail - - if send OK set `rentDueNotificationStatus` to `sent` - - if send failed set `rentDueNotificationStatus` to `failed` + - if send OK set `rentNotificationStatus` to `sent` + - if send failed set `rentNotificationStatus` to `failed` - decrement the e-mail budget counter ### Rent due notifications E-mail content @@ -132,15 +132,15 @@ The process of bills due notifications e-mail is as follows: - fetch all `BillingLocations` from `lokacije` collection (see `locationActions.ts` in `web-app` workspace) - filter only records which satisfy the following: - `yearMonth.year` and `yearMonth.month` equal to current year and month - `tenantEmailStatus` is equal to `EmailStatus.Verified` - - `billFwdEnabled === true` - - `billFwdStatus === 'pending'` + - `billsNotificationEnabled === true` + - `billsNotificationStatus === 'scheduled'` - for each record found - check the e-mail budget counter -> if the value is 0 then exit - compile an e-mail containing the content listed below - send the e-mail - - if send OK set `billFwdStatus` to `sent` - - if send failed set `billFwdStatus` to `failed` + - if send OK set `billsNotificationStatus` to `sent` + - if send failed set `billsNotificationStatus` to `failed` - decrement the e-mail budget counter ### Utility bills due notifications E-mail content diff --git a/sprints/sprint--confirm-unsubscribe.md b/sprints/sprint--confirm-unsubscribe.md index 393e46a..07b6e22 100644 --- a/sprints/sprint--confirm-unsubscribe.md +++ b/sprints/sprint--confirm-unsubscribe.md @@ -19,8 +19,8 @@ The web page served at this path contains an text explanation and "Verify e-mail The text includes the following information: * what the web app is about - very short into -* why the e-mail was sent = because the landloard of the property `BillingLocation.name` configured the rent (`BillingLocation.rentDueNotificationEnabled`) and/or utility bills (`BillingLocation.billFwdStrategy`) to be delivered to that e-mail address -* what will hapen if he/she clicks on the "Verify e-mail" button = they will be receiving rent due (`BillingLocation.rentDueNotificationEnabled`) or utility bills due (`BillingLocation.billFwdStrategy`) notification or both - 2x a month - depending on the config set by the landloard +* why the e-mail was sent = because the landloard of the property `BillingLocation.name` configured the rent (`BillingLocation.rentNotificationEnabled`) and/or utility bills (`BillingLocation.billsNotificationStrategy`) to be delivered to that e-mail address +* what will hapen if he/she clicks on the "Verify e-mail" button = they will be receiving rent due (`BillingLocation.rentNotificationEnabled`) or utility bills due (`BillingLocation.billsNotificationStrategy`) notification or both - 2x a month - depending on the config set by the landloard * opt-out infomation (they can ignore this e-mail, but can also opt-out at any moment) If the user clicks the button "Verify e-mail" this triggers update of `BillingLocation.tenantEmailStatus`. diff --git a/web-app/app/[locale]/share/proof-of-payment/combined/[id]/route.tsx b/web-app/app/[locale]/share/proof-of-payment/combined/[id]/route.tsx index f282a9e..7e25a5d 100644 --- a/web-app/app/[locale]/share/proof-of-payment/combined/[id]/route.tsx +++ b/web-app/app/[locale]/share/proof-of-payment/combined/[id]/route.tsx @@ -23,12 +23,12 @@ export async function GET(request: Request, { params: { id } }: { params: { id: const location = await dbClient.collection("lokacije") .findOne({ _id: locationID }, { projection: { - utilBillsProofOfPayment: 1, + billsProofOfPayment: 1, shareTTL: 1, } }); - if (!location?.utilBillsProofOfPayment) { + if (!location?.billsProofOfPayment) { notFound(); } @@ -38,7 +38,7 @@ export async function GET(request: Request, { params: { id } }: { params: { id: } // Convert fileContentsBase64 from Base64 string to binary - const fileContentsBuffer = Buffer.from(location.utilBillsProofOfPayment.fileContentsBase64, 'base64'); + const fileContentsBuffer = Buffer.from(location.billsProofOfPayment.fileContentsBase64, 'base64'); // Convert fileContentsBuffer to format that can be sent to the client const fileContents = new Uint8Array(fileContentsBuffer); @@ -47,8 +47,8 @@ export async function GET(request: Request, { params: { id } }: { params: { id: status: 200, headers: { 'Content-Type': 'application/pdf', - 'Content-Disposition': `attachment; filename="${location.utilBillsProofOfPayment.fileName}"`, - 'Last-Modified': `${location.utilBillsProofOfPayment.fileLastModified}` + 'Content-Disposition': `attachment; filename="${location.billsProofOfPayment.fileName}"`, + 'Last-Modified': `${location.billsProofOfPayment.fileLastModified}` } }); } diff --git a/web-app/app/lib/actions/billActions.ts b/web-app/app/lib/actions/billActions.ts index 1438dc2..6f370ca 100644 --- a/web-app/app/lib/actions/billActions.ts +++ b/web-app/app/lib/actions/billActions.ts @@ -2,7 +2,7 @@ import { z } from 'zod'; import { getDbClient } from '../dbClient'; -import { Bill, BilledTo, FileAttachment, BillingLocation } from '@evidencija-rezija/shared-code'; +import { Bill, BilledTo, FileAttachment, BillingLocation, BillsNotificationStatus } from '@evidencija-rezija/shared-code'; import { ObjectId } from 'mongodb'; import { withUser } from '@/app/lib/auth'; import { AuthenticatedUser } from '../types/next-auth'; @@ -13,7 +13,7 @@ import { unstable_noStore, revalidatePath } from 'next/cache'; import { extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code'; import { validatePdfFile } from '../validators/pdfValidator'; import { checkUploadRateLimit } from '../uploadRateLimiter'; -import { shouldUpdateBillFwdStatusWhenAttached, shouldUpdateBillFwdStatusWhenPayed } from '../billForwardingHelpers'; +import { shouldUpdateBillsNotificationStatusWhenAttached, shouldUpdateBillsNotificationStatusWhenPayed } from '../billForwardingHelpers'; export type State = { errors?: { @@ -178,7 +178,7 @@ export const updateOrAddBill = withUser(async (user: AuthenticatedUser, location const billAttachment = await serializeAttachment(attachmentFile); - // Fetch the location to check billFwdStatus conditions + // Fetch the location to check billsNotificationStatus conditions const location = await dbClient.collection("lokacije").findOne({ _id: locationId, userId @@ -188,9 +188,9 @@ export const updateOrAddBill = withUser(async (user: AuthenticatedUser, location return { success: false, error: 'Location not found' }; } - // Check if we should update billFwdStatus to "pending" - const shouldSetFwdPendingWhenAttached = shouldUpdateBillFwdStatusWhenAttached(location, billId, billAttachment !== null); - const shouldSetFwdPendingWhenPayed = shouldUpdateBillFwdStatusWhenPayed(location, billId, billPaid); + // Check if we should update billsNotificationStatus to `Scheduled` + const shouldSetFwdPendingWhenAttached = shouldUpdateBillsNotificationStatusWhenAttached(location, billId, billAttachment !== null); + const shouldSetFwdPendingWhenPayed = shouldUpdateBillsNotificationStatusWhenPayed(location, billId, billPaid); const shouldSetFwdPending = shouldSetFwdPendingWhenAttached || shouldSetFwdPendingWhenPayed; if (billId) { @@ -215,9 +215,9 @@ export const updateOrAddBill = withUser(async (user: AuthenticatedUser, location "bills.$[elem].hub3aText": hub3aText, }; - // Add billFwdStatus if needed + // Add billsNotificationStatus if needed if (shouldSetFwdPending) { - (mongoDbSet as any).billFwdStatus = "pending"; + (mongoDbSet as any).billsNotificationStatus = BillsNotificationStatus.Scheduled; } // update bill in given location with the given locationID @@ -253,10 +253,10 @@ export const updateOrAddBill = withUser(async (user: AuthenticatedUser, location } }; - // Add billFwdStatus update if needed + // Add billsNotificationStatus update if needed if (shouldSetFwdPending) { updateOp.$set = { - billFwdStatus: "pending" + billsNotificationStatus: BillsNotificationStatus.Scheduled }; } diff --git a/web-app/app/lib/actions/locationActions.ts b/web-app/app/lib/actions/locationActions.ts index ae75494..0bd3e65 100644 --- a/web-app/app/lib/actions/locationActions.ts +++ b/web-app/app/lib/actions/locationActions.ts @@ -2,7 +2,7 @@ import { z } from 'zod'; import { getDbClient } from '../dbClient'; -import { BillingLocation, FileAttachment, YearMonth, EmailStatus } from '@evidencija-rezija/shared-code'; +import { BillingLocation, FileAttachment, YearMonth, EmailStatus, BillsNotificationStrategy } from '@evidencija-rezija/shared-code'; import { ObjectId } from 'mongodb'; import { withUser } from '@/app/lib/auth'; import { AuthenticatedUser } from '../types/next-auth'; @@ -20,11 +20,11 @@ export type State = { tenantName?: string[]; tenantStreet?: string[]; tenantTown?: string[]; - billFwdEnabled?: string[]; + billsNotificationEnabled?: string[]; tenantEmail?: string[]; tenantEmailStatus?: string[]; - billFwdStrategy?: string[]; - rentDueNotificationEnabled?: string[]; + billsNotificationStrategy?: string[]; + rentNotificationEnabled?: string[]; rentDueDay?: string[]; rentAmount?: string[]; updateScope?: string[]; @@ -44,12 +44,12 @@ const FormSchema = (t:IntlTemplateFn) => z.object({ tenantName: z.string().max(30).optional().nullable(), tenantStreet: z.string().max(27).optional().nullable(), tenantTown: z.string().max(27).optional().nullable(), - billFwdEnabled: z.boolean().optional().nullable(), + billsNotificationEnabled: z.boolean().optional().nullable(), tenantEmail: z.string().email(t("tenant-email-invalid")).optional().or(z.literal("")).nullable(), tenantEmailStatus: z.enum([EmailStatus.Unverified, EmailStatus.VerificationPending, EmailStatus.Verified, EmailStatus.Unsubscribed]).optional().nullable(), tenantEmailLanguage: z.enum(["hr", "en"]).optional().nullable(), - billFwdStrategy: z.enum(["when-payed", "when-attached"]).optional().nullable(), - rentDueNotificationEnabled: z.boolean().optional().nullable(), + billsNotificationStrategy: z.enum([BillsNotificationStrategy.WhenPayed, BillsNotificationStrategy.WhenAttached]).optional().nullable(), + rentNotificationEnabled: z.boolean().optional().nullable(), rentDueDay: z.coerce.number().min(1).max(31).optional().nullable(), rentAmount: z.coerce.number().int(t("rent-amount-integer")).positive(t("rent-amount-positive")).optional().nullable(), addToSubsequentMonths: z.boolean().optional().nullable(), @@ -86,7 +86,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({ path: ["tenantTown"], }) .refine((data) => { - if (data.billFwdEnabled || data.rentDueNotificationEnabled) { + if (data.billsNotificationEnabled || data.rentNotificationEnabled) { return !!data.tenantEmail && data.tenantEmail.trim().length > 0; } return true; @@ -95,7 +95,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({ path: ["tenantEmail"], }) .refine((data) => { - if (data.rentDueNotificationEnabled) { + if (data.rentNotificationEnabled) { return !!data.rentAmount && data.rentAmount > 0; } return true; @@ -134,12 +134,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat tenantName: formData.get('tenantName') || null, tenantStreet: formData.get('tenantStreet') || null, tenantTown: formData.get('tenantTown') || null, - billFwdEnabled: formData.get('billFwdEnabled') === 'on', + billsNotificationEnabled: formData.get('billsNotificationEnabled') === 'on', tenantEmail: formData.get('tenantEmail') || null, tenantEmailStatus: formData.get('tenantEmailStatus') as "unverified" | "verification-pending" | "verified" | "unsubscribed" | undefined, tenantEmailLanguage: formData.get('tenantEmailLanguage') as "hr" | "en" | undefined, - billFwdStrategy: formData.get('billFwdStrategy') as "when-payed" | "when-attached" | undefined, - rentDueNotificationEnabled: formData.get('rentDueNotificationEnabled') === 'on', + billsNotificationStrategy: formData.get('billsNotificationStrategy') as BillsNotificationStrategy | undefined, + rentNotificationEnabled: formData.get('rentNotificationEnabled') === 'on', rentDueDay: formData.get('rentDueDay') || null, rentAmount: formData.get('rentAmount') || null, addToSubsequentMonths: formData.get('addToSubsequentMonths') === 'on', @@ -161,12 +161,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat tenantName, tenantStreet, tenantTown, - billFwdEnabled, + billsNotificationEnabled, tenantEmail, tenantEmailStatus, tenantEmailLanguage, - billFwdStrategy, - rentDueNotificationEnabled, + billsNotificationStrategy, + rentNotificationEnabled, rentDueDay, rentAmount, addToSubsequentMonths, @@ -220,12 +220,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, - billFwdEnabled: billFwdEnabled || false, + billsNotificationEnabled: billsNotificationEnabled || false, tenantEmail: tenantEmail || null, tenantEmailStatus: finalEmailStatus, tenantEmailLanguage: tenantEmailLanguage || null, - billFwdStrategy: billFwdStrategy || "when-payed", - rentDueNotificationEnabled: rentDueNotificationEnabled || false, + billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed, + rentNotificationEnabled: rentNotificationEnabled || false, rentDueDay: rentDueDay || null, rentAmount: rentAmount || null, } @@ -253,12 +253,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, - billFwdEnabled: billFwdEnabled || false, + billsNotificationEnabled: billsNotificationEnabled || false, tenantEmail: tenantEmail || null, tenantEmailStatus: finalEmailStatus, tenantEmailLanguage: tenantEmailLanguage || null, - billFwdStrategy: billFwdStrategy || "when-payed", - rentDueNotificationEnabled: rentDueNotificationEnabled || false, + billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed, + rentNotificationEnabled: rentNotificationEnabled || false, rentDueDay: rentDueDay || null, rentAmount: rentAmount || null, } @@ -279,12 +279,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, - billFwdEnabled: billFwdEnabled || false, + billsNotificationEnabled: billsNotificationEnabled || false, tenantEmail: tenantEmail || null, tenantEmailStatus: finalEmailStatus, tenantEmailLanguage: tenantEmailLanguage || null, - billFwdStrategy: billFwdStrategy || "when-payed", - rentDueNotificationEnabled: rentDueNotificationEnabled || false, + billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed, + rentNotificationEnabled: rentNotificationEnabled || false, rentDueDay: rentDueDay || null, rentAmount: rentAmount || null, } @@ -304,12 +304,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, - billFwdEnabled: billFwdEnabled || false, + billsNotificationEnabled: billsNotificationEnabled || false, tenantEmail: tenantEmail || null, tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified, tenantEmailLanguage: tenantEmailLanguage || null, - billFwdStrategy: billFwdStrategy || "when-payed", - rentDueNotificationEnabled: rentDueNotificationEnabled || false, + billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed, + rentNotificationEnabled: rentNotificationEnabled || false, rentDueDay: rentDueDay || null, rentAmount: rentAmount || null, yearMonth: yearMonth, @@ -381,12 +381,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, - billFwdEnabled: billFwdEnabled || false, + billsNotificationEnabled: billsNotificationEnabled || false, tenantEmail: tenantEmail || null, tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified, tenantEmailLanguage: tenantEmailLanguage || null, - billFwdStrategy: billFwdStrategy || "when-payed", - rentDueNotificationEnabled: rentDueNotificationEnabled || false, + billsNotificationStrategy: billsNotificationStrategy || BillsNotificationStrategy.WhenPayed, + rentNotificationEnabled: rentNotificationEnabled || false, rentDueDay: rentDueDay || null, rentAmount: rentAmount || null, yearMonth: { year: monthData.year, month: monthData.month }, @@ -495,8 +495,8 @@ export const fetchAllLocations = withUser(async (user:AuthenticatedUser, year:nu // "bills.hub3aText": 1, // project only file name - leave out file content so that // less data is transferred to the client - "utilBillsProofOfPayment.fileName": 1, - "utilBillsProofOfPayment.uploadedAt": 1, + "billsProofOfPayment.fileName": 1, + "billsProofOfPayment.uploadedAt": 1, }, }, { @@ -558,7 +558,7 @@ export const fetchLocationById = async (locationID:string) => { projection: { // don't include the attachment binary data in the response "bills.attachment.fileContentsBase64": 0, - "utilBillsProofOfPayment.fileContentsBase64": 0, + "billsProofOfPayment.fileContentsBase64": 0, }, } ); @@ -691,7 +691,7 @@ const serializeAttachment = async (file: File | null):Promise("lokacije") - .findOne({ _id: locationID }, { projection: { userId: 1, utilBillsProofOfPayment: 1, shareTTL: 1 } }); + .findOne({ _id: locationID }, { projection: { userId: 1, billsProofOfPayment: 1, shareTTL: 1 } }); if (!location || !location.userId) { return { success: false, error: 'Invalid request' }; @@ -741,12 +741,12 @@ export const uploadUtilBillsProofOfPayment = async ( } // Check if proof of payment already uploaded - if (location.utilBillsProofOfPayment) { + if (location.billsProofOfPayment) { return { success: false, error: 'Proof of payment already uploaded for this location' }; } // 4. FILE VALIDATION - const file = formData.get('utilBillsProofOfPayment') as File; + const file = formData.get('billsProofOfPayment') as File; if (!file || file.size === 0) { return { success: false, error: 'No file provided' }; @@ -770,7 +770,7 @@ export const uploadUtilBillsProofOfPayment = async ( .updateOne( { _id: locationID }, { $set: { - utilBillsProofOfPayment: attachment + billsProofOfPayment: attachment } } ); @@ -786,7 +786,7 @@ export const uploadUtilBillsProofOfPayment = async ( /** * Upload rent proof of payment (for tenants via share link) - * Similar to uploadUtilBillsProofOfPayment but for rent payments + * Similar to uploadBillsProofOfPayment but for rent payments */ export const uploadRentProofOfPayment = async ( shareId: string, diff --git a/web-app/app/lib/actions/monthActions.ts b/web-app/app/lib/actions/monthActions.ts index 6df1b4d..7d729a8 100644 --- a/web-app/app/lib/actions/monthActions.ts +++ b/web-app/app/lib/actions/monthActions.ts @@ -2,13 +2,13 @@ import { getDbClient } from '../dbClient'; import { ObjectId } from 'mongodb'; -import { Bill, BillingLocation, YearMonth } from '@evidencija-rezija/shared-code'; +import { Bill, BillingLocation, BillsNotificationStatus, YearMonth } from '@evidencija-rezija/shared-code'; import { AuthenticatedUser } from '../types/next-auth'; import { withUser } from '../auth'; import { unstable_noStore as noStore, unstable_noStore, revalidatePath } from 'next/cache'; import { getLocale } from 'next-intl/server'; import { gotoHomeWithMessage } from './navigationActions'; -import { shouldUpdateBillFwdStatusWhenPayed } from '../billForwardingHelpers'; +import { shouldUpdateBillsNotificationStatusWhenPayed } from '../billForwardingHelpers'; /** * Server-side action which adds a new month to the database @@ -44,7 +44,7 @@ export const addMonth = withUser(async (user:AuthenticatedUser, { year, month }: ...prevLocation, // clear properties specific to the month seenByTenantAt: undefined, - utilBillsProofOfPayment: undefined, + billsProofOfPayment: undefined, // assign a new ID _id: (new ObjectId()).toHexString(), yearMonth: { @@ -234,11 +234,11 @@ export const updateMonth = withUser(async ( // Check if any paid bill triggers forwarding (only need to check once) const firstPaidUpdate = locationUpdates.find(update => update.paid === true); - if (firstPaidUpdate && shouldUpdateBillFwdStatusWhenPayed(location, firstPaidUpdate.billId, true)) { - // Update billFwdStatus to "pending" + if (firstPaidUpdate && shouldUpdateBillsNotificationStatusWhenPayed(location, firstPaidUpdate.billId, true)) { + // Update billsNotificationStatus to "scheduled" await dbClient.collection("lokacije").updateOne( { _id: locationId }, - { $set: { billFwdStatus: "pending" } } + { $set: { billsNotificationStatus: BillsNotificationStatus.Scheduled } } ); } } diff --git a/web-app/app/lib/billForwardingHelpers.ts b/web-app/app/lib/billForwardingHelpers.ts index 9226882..a47ae74 100644 --- a/web-app/app/lib/billForwardingHelpers.ts +++ b/web-app/app/lib/billForwardingHelpers.ts @@ -1,13 +1,13 @@ -import { BillingLocation, BilledTo } from '@evidencija-rezija/shared-code'; +import { BillingLocation, BilledTo, BillsNotificationStatus, BillsNotificationStrategy } from '@evidencija-rezija/shared-code'; /** - * Checks if billFwdStatus should be updated to "pending" based on attachment status + * Checks if billsNotificationStatus should be updated to `Scheduled` based on attachment status * @param location - The billing location containing the bill * @param currentBillId - The ID of the bill being updated (to exclude from check) * @param hasNewAttachment - Whether a new attachment is being added to the current bill - * @returns true if billFwdStatus should be set to "pending" + * @returns true if billsNotificationStatus should be set to `Scheduled` */ -export const shouldUpdateBillFwdStatusWhenAttached = ( +export const shouldUpdateBillsNotificationStatusWhenAttached = ( location: BillingLocation, currentBillId: string | undefined, hasNewAttachment: boolean @@ -17,18 +17,18 @@ export const shouldUpdateBillFwdStatusWhenAttached = ( return false; } - // Check billFwdEnabled is true - if (location.billFwdEnabled !== true) { + // Check billsNotificationEnabled is true + if (location.billsNotificationEnabled !== true) { return false; } - // Check billFwdStrategy is "when-attached" - if (location.billFwdStrategy !== "when-attached") { + // Check billsNotificationStrategy is `WhenAttached` + if (location.billsNotificationStrategy !== BillsNotificationStrategy.WhenAttached) { return false; } - // Check bills have already been sent or are pending -> don't sent them again - if (location.billFwdStatus === "pending" || location.billFwdStatus === "sent") { + // Check bills have already been sent or are scheduled -> don't send them again + if (location.billsNotificationStatus === BillsNotificationStatus.Scheduled || location.billsNotificationStatus === BillsNotificationStatus.Sent) { return false; } @@ -49,13 +49,13 @@ export const shouldUpdateBillFwdStatusWhenAttached = ( }; /** - * Checks if billFwdStatus should be updated to "pending" based on paid status + * Checks if billsNotificationStatus should be updated to "scheduled" based on paid status * @param location - The billing location containing the bill * @param currentBillId - The ID of the bill being updated (to exclude from check) * @param isPaid - Whether the current bill is being marked as paid - * @returns true if billFwdStatus should be set to "pending" + * @returns true if billsNotificationStatus should be set to "scheduled" */ -export const shouldUpdateBillFwdStatusWhenPayed = ( +export const shouldUpdateBillsNotificationStatusWhenPayed = ( location: BillingLocation, currentBillId: string | undefined, isPaid: boolean @@ -65,18 +65,18 @@ export const shouldUpdateBillFwdStatusWhenPayed = ( return false; } - // Check billFwdEnabled is true - if (location.billFwdEnabled !== true) { + // Check billsNotificationEnabled is true + if (location.billsNotificationEnabled !== true) { return false; } - // Check billFwdStrategy is "when-payed" - if (location.billFwdStrategy !== "when-payed") { + // Check billsNotificationStrategy is `WhenPayed` + if (location.billsNotificationStrategy !== BillsNotificationStrategy.WhenPayed) { return false; } - // Check bills have already been sent or are pending -> don't sent them again - if (location.billFwdStatus === "pending" || location.billFwdStatus === "sent") { + // Check bills have already been sent or are scheduled -> don't sent them again + if (location.billsNotificationStatus === BillsNotificationStatus.Scheduled || location.billsNotificationStatus === BillsNotificationStatus.Sent) { return false; } diff --git a/web-app/app/lib/format.ts b/web-app/app/lib/format.ts index def9c9d..d23ec5f 100644 --- a/web-app/app/lib/format.ts +++ b/web-app/app/lib/format.ts @@ -1,4 +1,4 @@ -import { YearMonth } from "./db-types"; +import { YearMonth } from "@evidencija-rezija/shared-code"; export const formatYearMonth = ({ year, month }: YearMonth): string => { return `${year}-${month<10?"0":""}${month}`; diff --git a/web-app/app/ui/LocationCard.tsx b/web-app/app/ui/LocationCard.tsx index 8cc2137..3e84d7d 100644 --- a/web-app/app/ui/LocationCard.tsx +++ b/web-app/app/ui/LocationCard.tsx @@ -24,7 +24,7 @@ export const LocationCard: FC = ({ location, currency }) => { bills, seenByTenantAt, // NOTE: only the fileName is projected from the DB to reduce data transfer - utilBillsProofOfPayment, + billsProofOfPayment, tenantEmail, tenantEmailStatus, } = location; @@ -70,7 +70,7 @@ export const LocationCard: FC = ({ location, currency }) => { - { totalUnpaid > 0 || totalPayed > 0 || seenByTenantAt || utilBillsProofOfPayment?.uploadedAt || (tenantEmail && tenantEmailStatus && tenantEmailStatus !== EmailStatus.Verified) ? + { totalUnpaid > 0 || totalPayed > 0 || seenByTenantAt || billsProofOfPayment?.uploadedAt || (tenantEmail && tenantEmailStatus && tenantEmailStatus !== EmailStatus.Verified) ? <>
@@ -125,7 +125,7 @@ export const LocationCard: FC = ({ location, currency }) => {
)} - {utilBillsProofOfPayment?.uploadedAt && ( + {billsProofOfPayment?.uploadedAt && ( = ({ location, yearMont tenantEmailLanguage: location?.tenantEmailLanguage ?? (locale as "hr" | "en"), tenantPaymentMethod: location?.tenantPaymentMethod ?? "none", proofOfPaymentType: location?.proofOfPaymentType ?? "none", - billFwdEnabled: location?.billFwdEnabled ?? false, - billFwdStrategy: location?.billFwdStrategy ?? "when-payed", - rentDueNotificationEnabled: location?.rentDueNotificationEnabled ?? false, + billsNotificationEnabled: location?.billsNotificationEnabled ?? false, + billsNotificationStrategy: location?.billsNotificationStrategy ?? BillsNotificationStrategy.WhenPayed, + rentNotificationEnabled: location?.rentNotificationEnabled ?? false, rentAmount: location?.rentAmount ?? "", rentDueDay: location?.rentDueDay ?? 1, }); @@ -279,21 +279,21 @@ export const LocationEditForm: FC = ({ location, yearMont - {formValues.billFwdEnabled && ( + {formValues.billsNotificationEnabled && (
{t("utility-bill-forwarding-strategy-label")} - + +
)} @@ -307,16 +307,16 @@ export const LocationEditForm: FC = ({ location, yearMont - {formValues.rentDueNotificationEnabled && ( + {formValues.rentNotificationEnabled && (
{t("rent-due-day-label")} @@ -356,7 +356,7 @@ export const LocationEditForm: FC = ({ location, yearMont )}
- {(formValues.billFwdEnabled || formValues.rentDueNotificationEnabled) && ( + {(formValues.billsNotificationEnabled || formValues.rentNotificationEnabled) && (
{t("tenant-email-legend")}