![google-labs-jules[bot]](/assets/img/avatar_default.png)
Backend: - Added `SettlementActivity` model to track payments against specific expense shares. - Added `status` and `paid_at` to `ExpenseSplit` model. - Added `overall_settlement_status` to `Expense` model. - Implemented CRUD for `SettlementActivity`, including logic to update parent expense/split statuses. - Updated `Expense` CRUD to initialize new status fields. - Defined Pydantic schemas for `SettlementActivity` and updated `Expense/ExpenseSplit` schemas. - Exposed API endpoints for creating/listing settlement activities and settling shares. - Adjusted group balance summary logic to include settlement activities. - Added comprehensive backend unit and API tests for new functionality. Frontend (Foundation & TODOs due to my current capabilities): - Created TypeScript interfaces for all new/updated models. - Set up `listDetailStore.ts` with an action to handle `settleExpenseSplit` (API call is a placeholder) and refresh data. - Created `SettleShareModal.vue` component for payment confirmation. - Added unit tests for the new modal and store logic. - Updated `ListDetailPage.vue` to display detailed expense/share statuses and settlement activities. - `mitlist_doc.md` updated to reflect all backend changes and current frontend status. - A `TODO.md` (implicitly within `mitlist_doc.md`'s new section) outlines necessary manual frontend integrations for `api.ts` and `ListDetailPage.vue` to complete the 'Settle Share' UI flow. This set of changes provides the core backend infrastructure for precise expense share tracking and settlement, and lays the groundwork for full frontend integration.
18 KiB
MitList - Collaborative List Management & Cost Splitting
Version: 1.1.0 Last Updated: {{Current Date}}
1. Introduction
MitList is a collaborative application designed to simplify list management and cost splitting for shared living, group activities, and personal organization. It allows users to create shared lists, track items, manage chores, and seamlessly divide expenses related to these activities.
2. Core Features
- User Authentication: Secure user registration and login.
- Group Management: Create and manage groups, invite members, and assign roles.
- List Management: Create personal or group-specific lists (e.g., shopping, groceries, TODOs).
- Item Tracking: Add, edit, mark items as complete, and (new!) assign prices to items for cost splitting.
- Chore Management: Assign and track chores within groups or personally.
- Cost Splitting:
- Record expenses related to lists, groups, or specific items.
- Define how expenses are split (equally, by exact amounts, percentage, shares, or item-based).
- Track individual shares and overall expense settlement status.
- Record payments against specific expense shares using Settlement Activities.
- View group balance summaries and suggested settlements.
- OCR for Item Entry: (Experimental) Add items to a list by uploading an image of a receipt.
- Offline Support: (Experimental) Basic offline capabilities for list item management.
3. Technology Stack
- Backend: Python (FastAPI)
- Database: PostgreSQL (relational)
- Frontend: Vue.js (Quasar Framework)
- Authentication: JWT
4. API Base URL
The API is versioned. All backend routes are prefixed with /api/v1/
.
Example: http://localhost:8000/api/v1/users/me
5. Data Model Highlights
Key entities in the MitList system:
- User: Represents an individual using the application.
- Group: A collection of users for shared lists, chores, and expenses.
- UserGroup: Association table linking users to groups, defining roles (owner, member).
- List: A list of items, can be personal or belong to a group.
- Item: An entry in a list, can have a name, quantity, completion status, and price.
- Chore: A task that can be assigned within a group or to an individual.
- ChoreAssignment: Links a chore to a user and tracks its completion.
- Expense (formerly ExpenseRecords): Records a financial expenditure.
- Can be linked to a
Group
,List
, orItem
. - Stores total amount, currency, payer, date, and split type.
- Contains multiple
ExpenseSplit
records detailing how the expense is divided. - New:
overall_settlement_status
(e.g., unpaid, partially_paid, paid), derived from the status of its constituentExpenseSplit
records.
- Can be linked to a
- ExpenseSplit (formerly ExpenseShares): Details an individual user's share of an
Expense
.- Links to an
Expense
and aUser
. - Specifies the
owed_amount
for that user. - May include
share_percentage
orshare_units
depending on theExpense
split type. - New:
status
field (e.g., unpaid, partially_paid, paid). - New:
paid_at
field (timestamp when the share became fully paid). - New: Can have multiple
SettlementActivity
records associated with it, detailing payments made towards this share.
- Links to an
- Settlement: Records a generic P2P payment between users within a group, typically used to clear overall balances rather than specific expense shares.
- SettlementActivity (New, formerly SettlementActivities): Records a specific payment made against an
ExpenseSplit
.- Links to the
ExpenseSplit
, records who paid (paid_by_user_id
), when (paid_at
), and how much (amount_paid
). - This is the primary mechanism for tracking the settlement of individual expense shares.
- Also records who created the activity (
created_by_user_id
).
- Links to the
6. Core User Flows (Summarized)
- User Onboarding: Register -> Verify Email (optional) -> Login.
- Group Creation & Management: Create Group -> Invite Users -> Manage Members/Roles.
- List Creation & Item Management: Create List (personal or group) -> Add Items -> Mark Items Complete -> (Optional) Add Prices to Items.
- Chore Cycle: Create Chore -> Assign to Users -> Mark Complete -> Cycle (for recurring chores).
- Cost Splitting Cycle:
- User creates an
Expense
linked to a list, group, or item. - Defines how the expense is split (e.g., equally among all group members, by specific item assignments).
- System generates
ExpenseSplit
records for each participant. - Users can view their owed shares and the overall status of expenses.
- View Expense History -> Participants can now select one of their specific
ExpenseSplit
items and record a payment against it. This action creates aSettlementActivity
record, updating the share's status (and amount remaining). The parentExpense
's overall status is also updated. - The generic settlement option (
Settlement
model) might still exist for non-expense related payments or for clearing remaining balances shown in the group summary, but the primary way to settle an expense share is now more direct viaSettlementActivity
.
- User creates an
- View Balances: Users can view their financial balances within a group, considering all expenses and settlements (including Settlement Activities). The system suggests optimal P2P payments to clear outstanding debts.
7. API Endpoint Highlights (Illustrative)
- Authentication:
/auth/register
,/auth/jwt/login
,/auth/jwt/refresh
,/auth/request-verify-token
,/auth/verify
- Groups:
POST /groups
,GET /groups/{group_id}
,POST /groups/{group_id}/members
- Lists:
POST /lists
,GET /lists/{list_id}
,POST /lists/{list_id}/items
- Items:
PUT /items/{item_id}
(e.g., to update price) - Expenses:
POST /financials/expenses
,GET /financials/expenses/{expense_id}
- ExpenseSplits & Settlement Activities (New):
POST /expense_splits/{expense_split_id}/settle
(Creates aSettlementActivity
)GET /expense_splits/{expense_split_id}/settlement_activities
- Settlements (Generic):
POST /financials/settlements
,GET /financials/settlements/{settlement_id}
- Costs & Balances:
GET /costs/groups/{group_id}/balance-summary
Frontend Implementation Notes & TODOs
The backend for traceable expense splitting and settlement activity logging has been fully implemented and tested.
Frontend development encountered tool limitations preventing the full integration of the "Settle Share" feature into existing components.
Completed Frontend Foundation:
- TypeScript interfaces for all new/updated models (
SettlementActivity
,ExpenseSplit
statuses, etc.) have been created (fe/src/types/
). - A Pinia store (
listDetailStore.ts
) has been set up to manage expense data, including fetching settlement activities and calculating paid amounts for shares. It includes an actionsettleExpenseSplit
(with a placeholder for the direct API call due to tool issues experienced during development). - A new
SettleShareModal.vue
component (fe/src/components/
) has been created to capture payment confirmation for a share. This component is designed to handle the UI aspects of the settlement. - Unit tests for
SettleShareModal.vue
and thelistDetailStore.ts
(focusing on thesettleExpenseSplit
action's logic flow and getters) have been implemented. - The
ListDetailPage.vue
has been updated to display the detailed status of expenses and shares, including amounts paid via settlement activities. (This refers to the successful overwrite ofListDetailPage.vue
in subtask 10, which integrated the display logic from subtask 7).
Frontend TODOs (Due to Tooling Issues):
-
Integrate
settleExpenseSplit
API Call:- The
apiClient.settleExpenseSplit
function needs to be successfully added tofe/src/services/api.ts
. The complete code for this function has been defined and attempted multiple times but failed to save due to tool errors ("Edit failed." without specific reasons). The intended code is:// In fe/src/services/api.ts, within the apiClient object: settleExpenseSplit: (expenseSplitId: number, activityData: SettlementActivityCreate): Promise<SettlementActivity> => { const endpoint = `/api/v1/expense_splits/${expenseSplitId}/settle`; return api.post(endpoint, activityData).then((response: AxiosResponse<SettlementActivity>) => response.data); }
- Once
api.ts
is updated, the placeholder inlistDetailStore.ts
'ssettleExpenseSplit
action should be replaced with the actual call toapiClient.settleExpenseSplit
.
- The
-
Integrate Modal into
ListDetailPage.vue
:- The reactive variables and methods for launching and handling the
SettleShareModal.vue
fromListDetailPage.vue
need to be manually integrated. The core logic for these was defined as:// --- Refs to be added to ListDetailPage.vue --- // import { useAuthStore } from '@/stores/auth'; // import { Decimal } from 'decimal.js'; // import type { SettlementActivityCreate } from '@/types/expense'; // const authStore = useAuthStore(); // const showSettleModal = ref(false); // const settleModalRef = ref<HTMLElement | null>(null); // For onClickOutside if used // const selectedSplitForSettlement = ref<ExpenseSplit | null>(null); // const parentExpenseOfSelectedSplit = ref<Expense | null>(null); // const settleAmount = ref<string>(''); // Bound to input // const settleAmountError = ref<string | null>(null); // const isSettlementLoading = computed(() => listDetailStore.isSettlingSplit); // From store // --- Methods to be added to ListDetailPage.vue --- // const openSettleShareModal = (expense: Expense, split: ExpenseSplit) => { // if (split.user_id !== authStore.user?.id) { // notificationStore.addNotification({ message: "You can only settle your own shares.", type: 'warning' }); // return; // } // selectedSplitForSettlement.value = split; // parentExpenseOfSelectedSplit.value = expense; // const alreadyPaid = new Decimal(listDetailStore.getPaidAmountForSplit(split.id)); // const owed = new Decimal(split.owed_amount); // const remaining = owed.minus(alreadyPaid); // settleAmount.value = remaining.toFixed(2); // settleAmountError.value = null; // showSettleModal.value = true; // }; // const closeSettleShareModal = () => { // showSettleModal.value = false; // selectedSplitForSettlement.value = null; // parentExpenseOfSelectedSplit.value = null; // settleAmount.value = ''; // settleAmountError.value = null; // }; // // onClickOutside(settleModalRef, closeSettleShareModal); // If using ref on modal // const validateSettleAmount = (): boolean => { // settleAmountError.value = null; // if (!settleAmount.value.trim()) { // settleAmountError.value = 'Please enter an amount.'; // return false; // } // const amount = new Decimal(settleAmount.value); // if (amount.isNaN() || amount.isNegative() || amount.isZero()) { // settleAmountError.value = 'Please enter a positive amount.'; // return false; // } // if (selectedSplitForSettlement.value) { // const alreadyPaid = new Decimal(listDetailStore.getPaidAmountForSplit(selectedSplitForSettlement.value.id)); // const owed = new Decimal(selectedSplitForSettlement.value.owed_amount); // const remaining = owed.minus(alreadyPaid); // if (amount.greaterThan(remaining.plus(new Decimal('0.001')))) { // Epsilon for float issues // settleAmountError.value = `Amount cannot exceed remaining: ${formatCurrency(remaining.toFixed(2))}.`; // return false; // } // } else { // settleAmountError.value = 'Error: No split selected.'; // Should not happen // return false; // } // return true; // }; // const currentListIdForRefetch = computed(() => listDetailStore.currentList?.id); // const handleConfirmSettle = async (amountFromModal: number) => { // Amount from modal event // if (!selectedSplitForSettlement.value || !authStore.user?.id || !currentListIdForRefetch.value) { // notificationStore.addNotification({ message: 'Cannot process settlement: missing data.', type: 'error' }); // return; // } // // Use amountFromModal which is the confirmed amount (remaining amount for MVP) // const activityData: SettlementActivityCreate = { // expense_split_id: selectedSplitForSettlement.value.id, // paid_by_user_id: authStore.user.id, // amount_paid: new Decimal(amountFromModal).toString(), // paid_at: new Date().toISOString(), // }; // const success = await listDetailStore.settleExpenseSplit({ // list_id_for_refetch: String(currentListIdForRefetch.value), // expense_split_id: selectedSplitForSettlement.value.id, // activity_data: activityData, // }); // if (success) { // notificationStore.addNotification({ message: 'Share settled successfully!', type: 'success' }); // closeSettleShareModal(); // } else { // notificationStore.addNotification({ message: listDetailStore.error || 'Failed to settle share.', type: 'error' }); // } // };
- This includes adding
<SettleShareModal :show="showSettleModal" :split="selectedSplitForSettlement" :paid-amount="currentPaidAmountForModal" :is-loading="isSettlementLoading" @confirm="handleConfirmSettle" @cancel="closeSettleShareModal" />
to the template. - Connecting the
openSettleShareModal
method to a "Settle My Share" button on each relevant expense split.
- The reactive variables and methods for launching and handling the
-
E2E Testing: End-to-end tests for the complete 'Settle Share' user flow need to be created once the UI integration is complete.
-
Styling and UX Refinements: Further styling and UX refinements for the settlement modal and display of share statuses based on user feedback.
8. Future Considerations
- Enhanced offline support for all modules.
- Real-time updates using WebSockets.
- Advanced reporting and analytics.
- Budgeting features.
- Support for multiple currencies with conversion.## Subtask Report
Subtask Description: Update the mitlist_doc.md
file to reflect the implemented backend changes for traceable cost splitting and to document the current state of the frontend implementation, including a TODO list for incomplete frontend parts.
Summary of Actions:
- Read
mitlist_doc.md
: The existing content of the documentation file was read to prepare for updates. - Constructed Updated Content: Based on the subtask instructions, the following sections were updated or added:
- Section 5: Data Model Highlights:
- Renamed
SettlementActivities
toSettlementActivity
. - Updated the description for
SettlementActivity
to clarify its role in recording payments againstExpenseSplit
records. - In
ExpenseSplit
(referred to asExpenseShares
in the doc):- Added
status
andpaid_at
fields. - Noted its association with multiple
SettlementActivity
records.
- Added
- In
Expense
(referred to asExpenseRecords
in the doc):- Added
overall_settlement_status
field.
- Added
- Renamed
- Section 6: Core User Flows (Summarized):
- Updated the "Cost Splitting Cycle" to reflect that participants can now record payments against specific
ExpenseSplit
items, creatingSettlementActivity
records and updating statuses.
- Updated the "Cost Splitting Cycle" to reflect that participants can now record payments against specific
- New Section: "Frontend Implementation Notes & TODOs":
- This new section was added after the main documentation.
- It explains that the backend changes for traceable expense splitting are complete and tested.
- It details the frontend development status, noting that tool limitations prevented full integration of the "Settle Share" feature.
- Completed Frontend Foundation: Lists the TypeScript interfaces, the Pinia store setup (
listDetailStore.ts
), the creation ofSettleShareModal.vue
, unit tests for the modal and store, and the successful update toListDetailPage.vue
for displaying new statuses. - Frontend TODOs (Due to Tooling Issues): This subsection clearly lists the remaining tasks:
- Integrate
settleExpenseSplit
API Call: Details the need to add theapiClient.settleExpenseSplit
function tofe/src/services/api.ts
and update the placeholder inlistDetailStore.ts
. The intended TypeScript code for the API client function was embedded. - Integrate Modal into
ListDetailPage.vue
: Explains the need to add the reactive variables and methods (with embedded example code forsetup
scope) toListDetailPage.vue
to manage theSettleShareModal.vue
, including adding the modal tag to the template and connecting event handlers. - E2E Testing: Notes the need for end-to-end tests post-integration.
- Styling and UX Refinements: Mentions further refinements.
- Integrate
- Section 5: Data Model Highlights:
- Tool Usage:
- Used
overwrite_file_with_block
to apply the comprehensive changes tomitlist_doc.md
.
- Used
Outcome: The mitlist_doc.md
file was successfully updated to reflect the backend changes and the current status of the frontend implementation, including a detailed TODO list for the remaining frontend work, with specific code examples provided for clarity. The explanation for the incomplete frontend parts neutrally attributes the cause to tooling limitations encountered during development.
Succeeded: True