Advanced Software Engineering Team: Scott Schmidt, DaShawn Pfeifer, SeEun Chung, Suphanat Rojsiristith, Walter Zou
MVC is Model-View-Controller which is a way to organize your code into 3 roles.
- Model (Data) handles the database and data structure such as the tables users, invoices, and vendor.
- View (UI) is what the user sees such as forms and the dashboards. Example: Box Layout or Gidlayout
- Controller connects the model and view. Examples: mousePressed()
Here is our diagram MVC image below:
Advanced Software Engineering Team: Scott Schmidt, DaShawn Pfeifer, SeEun Chung, Suphanat Rojsiristith, Walter Zou
MVC is Model-View-Controller which is a way to organize your code into 3 roles.
- Model (Data) handles the database and data structure such as the tables users, invoices, and vendor.
- View (UI) is what the user sees such as forms and the dashboards. Example: Box Layout or Gidlayout
- Controller connects the model and view. Examples: mousePressed()
Here is our diagram MVC image below:

When a user interacts with the system (e.g., submits a form), the request starts in the Presentation Layer. The request is then passed to the Application Layer, where business logic and authentication are applied. The Application Layer communicates with the Data Layer to store or retrieve information from the database. The result is then returned back through the Application Layer to the Presentation Layer, where it is displayed to the user.
-Component1: Index Example: Display main content of application
- Component 2: Header Examples: Logo, title, navigation
- Component 3: Navigation Examples: Menus and forms
- Component 4: Route Pages
This is the “bain” of the application that contains business logic, rules, and actions:
- Component 4: CreateInvoice
- Component 5: CreateUser
- Component 6: getVendors
- Component 7: Login and Logout
- Component 6: Supabase Database Here are the tables, columns and connections in Supabase:
- View: React UI components such as Dashboard, Login/Register pages, Vendor List page, Add Vendor form, and Invoice form
- Controller: API routes and server functions including form handlers, authentication logic, and request processing
- Model: Database models and schemas such as User, Vendor, and Invoice using Supabase
1.The invoice page sends form data (vendor, date, amount) to the createInvoice function. 2. CreateInvoice checks the user and saves the invoice to the database. 3.The database saves the invoice and sends the result back. 4. The page shows success and redirects to the invoice list.
| Class/Method | Test Type | Requirement Tested | Mock Used | Expected Result |
|---|---|---|---|---|
VendorService.createVendor() |
Black-box unit | RQ-VEN-01: Reject blank vendor name |
FakeVendorRepository |
Throws Vendor name is required. |
VendorService.createVendor() |
Black-box unit | RQ-VEN-02: Create vendor with normalized address |
FakeVendorRepository |
Returns created=true, address assembled correctly |
VendorService.createVendor() |
Black-box unit | RQ-VEN-03: Prevent duplicate vendor insert by name |
FakeVendorRepository |
Returns existing vendor, created=false |
InvoiceService.calculateTotals() |
Black-box unit | RQ-INV-01: Compute subtotal/tax/total from line items |
None | Correct monetary totals returned |
InvoiceService.validateLineItems() |
Black-box unit | RQ-INV-02: Validate invalid line items |
None | Returns descriptive validation errors |
InvoiceApplicationService.createInvoiceForUser() |
Black-box unit | RQ-INV-03: Persist invoice using computed total only |
FakeInvoiceRepository |
Repository receives computed amount |
PaymentService.createVoucherPayment() |
Black-box unit | RQ-PAY-01: Fail when no invoices selected |
FakePaymentRepository |
Throws No invoices selected. |
PaymentService.createVoucherPayment() |
Black-box unit | RQ-PAY-02: Fail when invoice missing/already paid |
FakePaymentRepository |
Throws missing/already-paid invoice error |
PaymentService.createVoucherPayment() |
Black-box unit | RQ-PAY-03: Successful voucher/payment creation and invoice paid update |
FakePaymentRepository |
Payment created, links inserted, invoices marked paid |
| Core Class | Covered by Tests | Test File |
|---|---|---|
VendorService |
Yes | src/tests/vendor-service.test.ts |
InvoiceService |
Yes | src/tests/invoice-service.test.ts |
InvoiceApplicationService |
Yes | src/tests/invoice-service.test.ts |
PaymentService |
Yes | src/tests/payment-service.test.ts |
RQ-VEN-01: Vendor name cannot be blank; whitespace input fails.RQ-VEN-02: Valid vendor input creates a normalized address and returnscreated=true.RQ-VEN-03: Existing vendor name returns existing row (created=false) instead of reinserting.RQ-INV-01: Invoice totals are correctly computed from line items.RQ-INV-02: Invalid line-item fields return clear validation errors.RQ-INV-03: Invoice persistence uses computed total amount.RQ-PAY-01: Voucher creation fails when no invoices are selected.RQ-PAY-02: Missing or already-paid invoices are rejected.RQ-PAY-03: Successful voucher flow creates payment, links invoices, and updates paid status.
White-box algorithm: SandboxTransferEngine.transfer() in src/lib/banking/transfer.ts.
White-box test file: src/lib/banking/transfer.test.ts.
This test covers the main decision paths of the transfer method:
- valid transfer with zero fee
- valid transfer with transfer fee
- transfer fails when funds are insufficient
- transfer fails when source and destination accounts are the same
Main branches:
if (!invoiceIds.length)if (!input.accountId || !input.paymentDate || !payType)if (!ALLOWED_PAY_TYPES.has(payType))if (!invoiceRows.length)if (missingOrPaidIds.length)if (accountMismatch)if (shouldMarkInvoicesPaidNow(input.paymentDate))
flowchart TD
A[Start createVoucherPayment] --> B{Any invoice IDs?}
B -- No --> E1[No invoices selected]
B -- Yes --> C{Required fields present?}
C -- No --> E2[Missing required payment data]
C -- Yes --> D{Pay type allowed?}
D -- No --> E3[Invalid payment type]
D -- Yes --> F[Begin transaction]
F --> G[Load unpaid invoices]
G --> H{Any unpaid invoices loaded?}
H -- No --> E4[No valid unpaid invoices]
H -- Yes --> I{Any missing/already-paid IDs?}
I -- Yes --> E5[Missing/already-paid invoice error]
I -- No --> J{Account matches all invoices?}
J -- No --> E6[Account mismatch]
J -- Yes --> K[Calculate total + create payment + link invoices]
K --> L{Payment date <= today?}
L -- Yes --> M[Mark invoices paid]
L -- No --> N[Sync invoice paid status]
E1 --> T[THROW End of program]
E2 --> T
E3 --> T
E4 --> T
E5 --> T
E6 --> T
M --> O[Return success result]
N --> O
O --> S[SUCCESS End of program]
For PaymentService.createVoucherPayment():
- Decision points = 7
V(G) = D + 1 = 7 + 1 = 8
P1: No invoice IDs -> errorP2: Invoice IDs present but required fields missing -> errorP3: Invalid pay type -> errorP4: No unpaid invoice rows -> errorP5: Missing/already-paid IDs detected -> errorP6: Account mismatch -> errorP7: Success path withpaymentDate <= today-> mark paid -> successP8: Success path withpaymentDate > today-> sync status -> success
- Why this architecture fits the project: This architecture fits the project because it separates the user interface, business logic, and data management, making the ERP system easier to develop, test, and maintain.
- Any limitations/future improvements: A limitation is that the system can become more complex as the project grows, so future improvements could include clearer service separation, stronger validation, and better scalability support.

