Compare commits
1 Commits
master
...
feature-co
Author | SHA1 | Date | |
---|---|---|---|
cd02307a9d |
37
src/lib/db/db.ts
Normal file
37
src/lib/db/db.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { MONGO_URL } from '$env/static/private';
|
||||||
|
/*
|
||||||
|
0 - disconnected
|
||||||
|
1 - connected
|
||||||
|
2 - connecting
|
||||||
|
3 - disconnecting
|
||||||
|
4 - uninitialized
|
||||||
|
*/
|
||||||
|
const mongoConnection = {
|
||||||
|
isConnected: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dbConnect = async () => {
|
||||||
|
if (mongoConnection.isConnected === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mongoose.connections.length > 0) {
|
||||||
|
mongoConnection.isConnected = mongoose.connections[0].readyState;
|
||||||
|
if (mongoConnection.isConnected === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await mongoose.disconnect();
|
||||||
|
}
|
||||||
|
await mongoose.connect(MONGO_URL ?? '');
|
||||||
|
mongoConnection.isConnected = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dbDisconnect = async () => {
|
||||||
|
if (process.env.NODE_ENV === 'development') return;
|
||||||
|
if (mongoConnection.isConnected === 0) return;
|
||||||
|
|
||||||
|
await mongoose.disconnect();
|
||||||
|
mongoConnection.isConnected = 0;
|
||||||
|
};
|
25
src/lib/models/Payment.ts
Normal file
25
src/lib/models/Payment.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
const PaymentSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
type: {type: String, required: true, enum: ['payment', 'reimbursement']},
|
||||||
|
name: {type: String, required: true},
|
||||||
|
category : {type: String, required: false,},
|
||||||
|
date: {type: Date, default: Date.now},
|
||||||
|
images: [ {
|
||||||
|
mediapath: {type: String, required: false},
|
||||||
|
}],
|
||||||
|
description: {type: String, required: false},
|
||||||
|
note: {type: String, required: false},
|
||||||
|
tags : [String],
|
||||||
|
original_amount: {type: Number, required: true},
|
||||||
|
total_amount: {type: Number, required: true},
|
||||||
|
personal_amounts: [{
|
||||||
|
user: {type:String, required: true},
|
||||||
|
amount: {type: Number, required: true, default:0}
|
||||||
|
}],
|
||||||
|
currency: {type: String, required: true, default: 'CHF'},
|
||||||
|
}, {timestamps: true}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Payment= mongoose.model("Payment", PaymentSchema);
|
96
src/routes/api/cospend/+server.ts
Normal file
96
src/routes/api/cospend/+server.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { mkdir } from 'fs/promises';
|
||||||
|
import { Payment } from '$lib/models/Payment'; // adjust path as needed
|
||||||
|
import { dbConnect, dbDisconnect } from '$lib/db/db';
|
||||||
|
|
||||||
|
const UPLOAD_DIR = '/var/lib/www/static/test';
|
||||||
|
const BASE_CURRENCY = 'CHF'; // Default currency
|
||||||
|
|
||||||
|
export const POST: RequestHandler = async ({ request, locals }) => {
|
||||||
|
const formData = await request.formData();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const name = formData.get('name') as string;
|
||||||
|
const category = formData.get('category') as string;
|
||||||
|
const date= new Date(formData.get('date') as string);
|
||||||
|
const description = formData.get('description') as string;
|
||||||
|
const note = formData.get('note') as string;
|
||||||
|
const tags = JSON.parse(formData.get('tags') as string) as string[];
|
||||||
|
let currency = formData.get('currency') as string;
|
||||||
|
let original_amount = parseFloat(formData.get('original_amount') as string);
|
||||||
|
let total_amount = NaN;
|
||||||
|
|
||||||
|
// if currency is not BASE_CURRENCY, fetch current conversion rate using frankfurter API and date in YYYY-MM-DD format
|
||||||
|
if (!currency || currency === BASE_CURRENCY) {
|
||||||
|
currency = BASE_CURRENCY;
|
||||||
|
total_amount = parseFloat(formData.get('total_amount') as string);
|
||||||
|
} else {
|
||||||
|
const date_fmt = date.toISOString().split('T')[0]; // Convert date to YYYY-MM-DD format
|
||||||
|
// Fetch conversion rate logic here (not implemented in this example)
|
||||||
|
const res = await fetch(`https://api.frankfurter.app/${date_fmt}?from=${currency}&to=${BASE_CURRENCY}`)
|
||||||
|
const { result } = await res.json();
|
||||||
|
if (!result || !result[BASE_CURRENCY]) {
|
||||||
|
return new Response(JSON.stringify({ message: 'Currency conversion failed.' }), { status: 400 });
|
||||||
|
}
|
||||||
|
// Assuming you want to convert the total amount to BASE_CURRENCY
|
||||||
|
const conversionRate = parseFloat(result.rates[BASE_CURRENCY]);
|
||||||
|
alert(`Conversion rate from ${currency} to ${BASE_CURRENCY} on ${date_fmt}: ${conversionRate}`);
|
||||||
|
total_amount = original_amount * conversionRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const personal_amounts = JSON.parse(formData.get('personal_amounts') as string) as { user: string, amount: number }[];
|
||||||
|
|
||||||
|
|
||||||
|
if (!name || isNaN(total_amount)) {
|
||||||
|
return new Response(JSON.stringify({ message: 'Invalid required fields.' }), { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// await mkdir(UPLOAD_DIR, { recursive: true });
|
||||||
|
|
||||||
|
const images: { mediapath: string }[] = [];
|
||||||
|
const imageFiles = formData.getAll('images') as File[];
|
||||||
|
|
||||||
|
for (const file of imageFiles) {
|
||||||
|
const arrayBuffer = await file.arrayBuffer();
|
||||||
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
const safeName = `${Date.now()}_${file.name.replace(/[^a-zA-Z0-9_.-]/g, '_')}`;
|
||||||
|
const fullPath = path.join(UPLOAD_DIR, safeName);
|
||||||
|
//fs.writeFileSync(fullPath, buffer);
|
||||||
|
images.push({ mediapath: `/static/test/${safeName}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
await dbConnect();
|
||||||
|
const payment = new Payment({
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
date,
|
||||||
|
description,
|
||||||
|
note,
|
||||||
|
tags,
|
||||||
|
total_amount,
|
||||||
|
original_amount,
|
||||||
|
currency,
|
||||||
|
personal_amounts,
|
||||||
|
images
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// let auth = await locals.auth();
|
||||||
|
// // if(!auth){
|
||||||
|
// throw error(401, "Not logged in")
|
||||||
|
// }
|
||||||
|
|
||||||
|
try{
|
||||||
|
await Payment.create(payment);
|
||||||
|
} catch(e){
|
||||||
|
throw error(400, e)
|
||||||
|
}
|
||||||
|
await dbDisconnect();
|
||||||
|
return new Response(JSON.stringify({ message: 'Payment event created successfully.' }), { status: 201 });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return new Response(JSON.stringify({ message: 'Error processing request.' }), { status: 500 });
|
||||||
|
}
|
||||||
|
};
|
7
src/routes/cospend/+layout.server.ts
Normal file
7
src/routes/cospend/+layout.server.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { PageServerLoad } from "./$types"
|
||||||
|
|
||||||
|
export const load : PageServerLoad = (async ({locals}) => {
|
||||||
|
return {
|
||||||
|
session: await locals.auth(),
|
||||||
|
}
|
||||||
|
});
|
18
src/routes/cospend/+layout.svelte
Normal file
18
src/routes/cospend/+layout.svelte
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script>
|
||||||
|
import Header from '$lib/components/Header.svelte'
|
||||||
|
import UserHeader from '$lib/components/UserHeader.svelte';
|
||||||
|
export let data
|
||||||
|
let username = ""
|
||||||
|
if(data.user){
|
||||||
|
username = data.user.username
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<Header>
|
||||||
|
<ul class=site_header slot=links>
|
||||||
|
<li><a href="/glaube/gebete">Gebete</a></li>
|
||||||
|
<li><a href="/glaube/rosenkranz">Rosenkranz</a></li>
|
||||||
|
<li><a href="/glaube/predigten">Predigten</a></li>
|
||||||
|
</ul>
|
||||||
|
<UserHeader {username} slot=right_side></UserHeader>
|
||||||
|
<slot></slot>
|
||||||
|
</Header>
|
133
src/routes/cospend/+page.svelte
Normal file
133
src/routes/cospend/+page.svelte
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<style>
|
||||||
|
h1{
|
||||||
|
text-align: center;
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h1>Settlement Plan</h1>
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let name = '';
|
||||||
|
let category = '';
|
||||||
|
let date = new Date().toISOString().split('T')[0]; // format as yyyy-mm-dd
|
||||||
|
let images: File[] = [];
|
||||||
|
let description = '';
|
||||||
|
let note = '';
|
||||||
|
let tags = '';
|
||||||
|
let original_amount = 0;
|
||||||
|
let currency = 'CHF';
|
||||||
|
let payment_method = '';
|
||||||
|
|
||||||
|
let personal_amounts = [
|
||||||
|
{ user: 'alexander', amount: 0 },
|
||||||
|
{ user: 'anna', amount: 0 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('name', name);
|
||||||
|
formData.append('category', category);
|
||||||
|
formData.append('dateCreated', date);
|
||||||
|
formData.append('description', description);
|
||||||
|
formData.append('note', note);
|
||||||
|
formData.append('tags', JSON.stringify(tags.split(',').map(tag => tag.trim())));
|
||||||
|
formData.append('total_amount', total_amount.toString());
|
||||||
|
formData.append('currency', currency);
|
||||||
|
formData.append('payment_method', payment_method);
|
||||||
|
formData.append('personal_amounts', JSON.stringify(personal_amounts));
|
||||||
|
|
||||||
|
images.forEach((file, index) => {
|
||||||
|
formData.append('images', file);
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await fetch('/api/cospend/add', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await res.json();
|
||||||
|
alert(result.message);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form on:submit|preventDefault={handleSubmit} class="flex flex-col gap-4 max-w-xl">
|
||||||
|
<label>
|
||||||
|
Name:
|
||||||
|
<input type="text" bind:value={name} required />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Category:
|
||||||
|
<input type="text" bind:value={category} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Date Created:
|
||||||
|
<input type="date" bind:value={date} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Images:
|
||||||
|
<input type="file" multiple accept="image/*" on:change={(e) => images = Array.from(e.target.files)} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Description:
|
||||||
|
<textarea bind:value={description}></textarea>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Note:
|
||||||
|
<textarea bind:value={note}></textarea>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Tags (comma separated):
|
||||||
|
<input type="text" bind:value={tags} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Total Amount:
|
||||||
|
<input type="number" bind:value={original_amount} step="0.01" required />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Personal Amounts</legend>
|
||||||
|
{#each personal_amounts as entry, i}
|
||||||
|
<div class="flex gap-2 items-center">
|
||||||
|
<label>{entry.user}</label>
|
||||||
|
<input type="number" bind:value={personal_amounts[i].amount} step="0.01" required />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Currency:
|
||||||
|
<select bind:value={currency}>
|
||||||
|
<option value="CHF">CHF</option>
|
||||||
|
<option value="EUR">EUR</option>
|
||||||
|
<option value="USD">USD</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Payment Method:
|
||||||
|
<select bind:value={payment_method}>
|
||||||
|
<option value="">-- Select --</option>
|
||||||
|
<option value="cash">Cash</option>
|
||||||
|
<option value="bank_transfer">Bank Transfer</option>
|
||||||
|
<option value="credit_card">Credit Card</option>
|
||||||
|
<option value="twint">Twint</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit">Save Payment</button>
|
||||||
|
</form>
|
Loading…
x
Reference in New Issue
Block a user