Skip to content

Commit

Permalink
Merge pull request #49 from VaquitApp/feature/Registrar-Pago-Personal
Browse files Browse the repository at this point in the history
Registrar Pago Personal [5]
  • Loading branch information
MegaRedHand authored Jun 12, 2024
2 parents c161e22 + a882dc3 commit 1fd270c
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 31 deletions.
7 changes: 7 additions & 0 deletions my-app/src/lib/server/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ export const spendingService = {
: post('spending', data, getAuthHeader(cookies)),
list: (groupId: Id, cookies: Cookies) => get(`group/${groupId}/spending`, getAuthHeader(cookies))
};
export const paymentService = {
save: (data: Payment, cookies: Cookies) =>
data.id > 0
? put(`payment/${data.id}`, data, getAuthHeader(cookies))
: post('payment', data, getAuthHeader(cookies)),
list: (groupId: Id, cookies: Cookies) => get(`group/${groupId}/payment`, getAuthHeader(cookies))
};
export const budgetService = {
save: (data: Budget, cookies: Cookies) =>
data.id > 0
Expand Down
3 changes: 3 additions & 0 deletions my-app/src/lib/svgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ export const CAUTION_SVG =

export const WARNING_SVG =
'<svg style="display: inline-block; vertical-align: inherit" viewBox="0 0 16 16" width="20" height="20"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>';

export const ARROW_DOLLAR_SVG =
'<svg width=24 height=24 viewBox="0 0 24 24" fill="none"><g stroke-width="0"></g><g stroke-linecap="round" stroke-linejoin="round"></g><g> <path d="M13.3085 0.293087C13.699 -0.0976958 14.3322 -0.0976956 14.7227 0.293087L17.7186 3.29095C18.1091 3.68175 18.1091 4.31536 17.7185 4.70613L14.716 7.71034C14.3255 8.10113 13.6923 8.10113 13.3018 7.71034C12.9113 7.31956 12.9113 6.68598 13.3018 6.2952L14.6087 4.98743L7 4.98743C6.44771 4.98743 6 4.53942 6 3.98677C6 3.43412 6.44771 2.98611 7 2.98611L14.5855 2.9861L13.3085 1.70824C12.918 1.31745 12.918 0.683869 13.3085 0.293087Z" fill="#0F0F0F"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M12 20.998C14.2091 20.998 16 19.206 16 16.9954C16 14.7848 14.2091 12.9927 12 12.9927C9.79086 12.9927 8 14.7848 8 16.9954C8 19.206 9.79086 20.998 12 20.998ZM12 19.0934C10.842 19.0934 9.90331 18.1541 9.90331 16.9954C9.90331 15.8366 10.842 14.8973 12 14.8973C13.158 14.8973 14.0967 15.8366 14.0967 16.9954C14.0967 18.1541 13.158 19.0934 12 19.0934Z" fill="#0F0F0F"></path> <path d="M7 16.9954C7 17.548 6.55229 17.996 6 17.996C5.44772 17.996 5 17.548 5 16.9954C5 16.4427 5.44772 15.9947 6 15.9947C6.55229 15.9947 7 16.4427 7 16.9954Z" fill="#0F0F0F"></path> <path d="M19 16.9954C19 17.548 18.5523 17.996 18 17.996C17.4477 17.996 17 17.548 17 16.9954C17 16.4427 17.4477 15.9947 18 15.9947C18.5523 15.9947 19 16.4427 19 16.9954Z" fill="#0F0F0F"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M21 9.99074C22.6569 9.99074 24 11.3348 24 12.9927V20.998C24 22.656 22.6569 24 21 24H3C1.34315 24 0 22.656 0 20.998V12.9927C0 11.3348 1.34315 9.99074 3 9.99074H21ZM4 11.9921H20C20 12.2549 20.0517 12.5151 20.1522 12.7579C20.2528 13.0007 20.4001 13.2214 20.5858 13.4072C20.7715 13.593 20.992 13.7405 21.2346 13.841C21.4773 13.9416 21.7374 13.9934 22 13.9934V19.9974C21.7374 19.9974 21.4773 20.0491 21.2346 20.1497C20.992 20.2503 20.7715 20.3977 20.5858 20.5835C20.4001 20.7694 20.2528 20.99 20.1522 21.2328C20.0517 21.4756 20 21.7359 20 21.9987H4C4 21.7359 3.94827 21.4756 3.84776 21.2328C3.74725 20.99 3.59993 20.7694 3.41421 20.5835C3.2285 20.3977 3.00802 20.2503 2.76537 20.1497C2.52272 20.0491 2.26264 19.9974 2 19.9974V13.9934C2.26264 13.9934 2.52272 13.9416 2.76537 13.841C3.00802 13.7405 3.2285 13.593 3.41421 13.4072C3.59993 13.2214 3.74725 13.0007 3.84776 12.7579C3.94827 12.5151 4 12.2549 4 11.9921Z" fill="#0F0F0F"></path> </g></svg>';
8 changes: 8 additions & 0 deletions my-app/src/routes/api/members/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { groupService } from '$lib/server/api';
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async ({ url, cookies }) => {
const groupId = url.searchParams.get('groupId') || 0;
const body = await groupService.listAllMembers(+groupId, cookies);
return new Response(JSON.stringify(body));
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,28 @@ export const actions: Actions = {
const data = await request.formData();
const dateIni = new Date(data.get('dateIni') as string);
const dateFin = new Date(data.get('dateFin') as string);

const spendings: Spending[] = await spendingService.list(id, cookies);

const filteredSpendings = spendings.filter((spending: Spending) => {
const spendingDate = new Date(spending.date.slice(0,10)); //removing timezone
const filteredSpendings = spendings.filter((spending: Spending) => {
const spendingDate = new Date(spending.date.slice(0, 10)); //removing timezone
return spendingDate >= dateIni && spendingDate <= dateFin;
});

const totalSum = filteredSpendings.reduce((sum: number, spending: Spending) => sum + spending.amount, 0);
const totalSum = filteredSpendings.reduce(
(sum: number, spending: Spending) => sum + spending.amount,
0
);

const sumPerCategory = filteredSpendings.reduce((acc: { [x: number]: number; }, spending: Spending) => {
acc[spending.category_id] = (acc[spending.category_id] || 0) + spending.amount;
return acc;
}, {});
const sumPerCategory = filteredSpendings.reduce(
(acc: { [x: number]: number }, spending: Spending) => {
acc[spending.category_id] = (acc[spending.category_id] || 0) + spending.amount;
return acc;
},
{}
);

return {
return {
totalSum,
sumPerCategory
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@
const last_week = new Date();
last_week.setDate(now.getDay() - 7);
let initDate = last_week.toJSON()
let finDate = now.toJSON()
let initDate = last_week.toJSON();
let finDate = now.toJSON();
function getCategoryName(id: number): string {
return data.categories.filter(
(category: Category) => {
return category.id == id
}
)[0].name
function getCategoryName(id: Number): string {
return data.categories.filter((category: Category) => {
return category.id == id;
})[0].name;
}
</script>

<svelte:head>
Expand Down
12 changes: 10 additions & 2 deletions my-app/src/routes/groups/movements/[id=integer]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { budgetService, categoryService, groupService, spendingService } from '$lib/server/api';
import {
budgetService,
categoryService,
groupService,
paymentService,
spendingService
} from '$lib/server/api';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ cookies, params }) => {
Expand All @@ -7,13 +13,15 @@ export const load: PageServerLoad = async ({ cookies, params }) => {
const spendings: Spending[] = await spendingService.list(id, cookies);
const budgets: Budget[] = await budgetService.list(id, cookies);
const categories: Category[] = await categoryService.list(id, cookies);
const payments: Payment[] = await paymentService.list(id, cookies);
const members: User[] = await groupService.listAllMembers(id, cookies);

const categoryBalances: CategoryBalance[] = computeBalancesPerCategory(
spendings,
budgets,
categories
);
return { group, spendings, budgets, categories, categoryBalances };
return { group, spendings, payments, budgets, categories, members, categoryBalances };
};

function computeBalancesPerCategory(
Expand Down
62 changes: 51 additions & 11 deletions my-app/src/routes/groups/movements/[id=integer]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script lang="ts">
import { BUDGET_NEAR_LIMIT_THRESHOLD, title } from '$lib';
import { formatDateTimeString, formatMoney } from '$lib/formatter';
import { CAUTION_SVG, WARNING_SVG, pencil_svg } from '$lib/svgs';
import { ARROW_DOLLAR_SVG, CAUTION_SVG, WARNING_SVG, pencil_svg } from '$lib/svgs';
import type { PageServerData } from './$types';
export let data: PageServerData;
const movements = [...data.spendings, ...data.payments];
movements.sort((a, b) => Date.parse(b.date) - Date.parse(a.date));
const totalBudgets = data?.categoryBalances.reduce((acc, { budgets }) => acc + budgets, 0);
const totalSpendings = data?.categoryBalances.reduce((acc, { spendings }) => acc + spendings, 0);
const totalBalance = totalBudgets - totalSpendings;
Expand All @@ -31,9 +34,23 @@
return categoryList.map(({ categoryName }) => `"${categoryName}"`).join(', ');
}
const categoryNameById = Object.fromEntries(data.categories.map(({ id, name }) => [id, name]));
function getCategoryNameById(id: number) {
return categoryNameById[id];
}
const userEmailById = Object.fromEntries(data.members.map(({ id, email }) => [id, email]));
function getUserEmailById(id: number) {
return userEmailById[id];
}
function is_spending(movement: Spending | Payment) {
return 'category_id' in movement;
}
let categoryFilters: Id[] = [];
$: filteredSpendings = data.spendings.filter(
(s) => categoryFilters.length === 0 || categoryFilters.includes(s.category_id)
$: filteredMovements = movements.filter(
(m) =>
categoryFilters.length === 0 || (is_spending(m) && categoryFilters.includes(m.category_id))
);
function toggleCategoryFilter(categoryId: Id, shouldFilter: boolean) {
Expand Down Expand Up @@ -76,6 +93,7 @@
<li><a href="/spendings/details?groupId={data.group.id}">Añadir gasto</a></li>
<li><a href="/budgets/details?groupId={data.group.id}">Añadir presupuesto</a></li>
<li><a href="/categories/details?groupId={data.group.id}">Añadir categoría</a></li>
<li><a href="/payments/details?groupId={data.group.id}">Añadir pago</a></li>
</ul>
</details>
</div>
Expand Down Expand Up @@ -129,14 +147,36 @@
</div>
</article>

{#each filteredSpendings as spending}
<article class="grid">
<p>{formatDateTimeString(spending.date)}</p>
<!-- TODO: show category name -->
<p>{spending.category_id}</p>
<p>{spending.description}</p>
<p class="text-right">{formatMoney(spending.amount)}</p>
</article>
<article class="grid">
<b>Fecha</b>
<b>Pago/Gasto</b>
<b>De/Categoría</b>
<b>A/Descripción</b>
<b class="text-right">Monto</b>
</article>

{#each filteredMovements as movement}
{#if is_spending(movement)}
{@const spending = movement}
<article class="grid">
<p>{formatDateTimeString(spending.date)}</p>
<p>Gasto</p>
<p>{getCategoryNameById(spending.category_id)}</p>
<p>{spending.description}</p>
<p class="text-right">{formatMoney(spending.amount)}</p>
</article>
{:else}
{@const payment = movement}
<article class="grid">
<p>{formatDateTimeString(payment.date)}</p>
<p>Pago</p>
<p>
{getUserEmailById(payment.from_id)} &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {@html ARROW_DOLLAR_SVG}
</p>
<p>{getUserEmailById(payment.to_id)}</p>
<p class="text-right">{formatMoney(payment.amount)}</p>
</article>
{/if}
{/each}

<style>
Expand Down
33 changes: 33 additions & 0 deletions my-app/src/routes/payments/details/[[id=integer]]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { error, redirect } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types';
import { groupService, paymentService } from '$lib/server/api';
import { getUserId } from '$lib/auth';

export const load: PageServerLoad = async ({ params, url, cookies }) => {
const groupId = url.searchParams.get('groupId') || '';
// const id = Number(params.id) || 0;
const groups: Group[] = await groupService.list(cookies);
const userId = getUserId(cookies);
return { userId, groupId, groups };
};

export const actions: Actions = {
default: async ({ cookies, request, params }) => {
const id = Number(params.id) || 0;
const data = await request.formData();

const group_id = Number(data.get('groupId'));
const to_id = Number(data.get('payeeId'));
const from_id = getUserId(cookies);
const amount = Number(data.get('amount'));

if (!group_id) {
throw error(400, 'Group is required');
}

const payment: Payment = { id, group_id, from_id, to_id, amount };
await paymentService.save(payment, cookies);

redirect(302, `/groups/movements/${group_id}`);
}
};
62 changes: 62 additions & 0 deletions my-app/src/routes/payments/details/[[id=integer]]/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script lang="ts">
import { title } from '$lib';
import { onMount } from 'svelte';
import type { PageData } from './$types';
export let data: PageData;
export let members: User[] = [];
async function updateMembers(groupId: number) {
members = [];
if (groupId != 0) {
try {
const response = await fetch(`/api/members?groupId=${groupId}`);
members = await response.json();
} catch {}
}
}
onMount(async () => {
await updateMembers(data.groupId);
});
</script>

<svelte:head>
<title>{title} - Nuevo Pago</title>
</svelte:head>

<nav aria-label="breadcrumb">
<ul>
<li><a href="/groups">Grupos</a></li>
<li>Pagos</li>
</ul>
</nav>

<h2>Registrando Pago</h2>
<form method="POST">
<fieldset>
<label>
Ingrese un grupo para el pago
<select name="groupId" required value={data.groupId}>
{#each data.groups as group}
<option value={group.id}>{group.name}</option>
{/each}
</select>
</label>
<label>
Ingrese el receptor del pago
<select name="payeeId" required>
{#each members as member}
<option disabled={member.id === data.userId} value={member.id}>{member.email}</option>
{/each}
</select>
</label>
<label>
Ingrese el monto del pago
<input type="number" name="amount" placeholder="Monto" required />
</label>
<button>Crear</button>
<button type="button" class="outline" on:click={() => history.back()}>Cancelar</button>
</fieldset>
</form>
8 changes: 8 additions & 0 deletions my-app/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ declare global {
amount: number;
date: string;
};
type Payment = {
id: Id;
group_id: Id;
from_id: Id;
to_id: Id;
amount: number;
date: string;
};
type Invite = {
id: Id;
sender_id: Id;
Expand Down

0 comments on commit 1fd270c

Please sign in to comment.