Compare commits

..

2 Commits

Author SHA1 Message Date
Philipp Lang 11fe883b5c Remove split payments
continuous-integration/drone/push Build is failing Details
2023-12-20 22:11:07 +01:00
Philipp Lang acf280346f Add migration for invoices 2023-12-20 21:45:37 +01:00
11 changed files with 95 additions and 55 deletions

View File

@ -15,15 +15,7 @@ class Subscription extends Model
/**
* @var array<int, string>
*/
public $fillable = ['name', 'fee_id', 'split', 'for_promise'];
/**
* @var array<string, string>
*/
public $casts = [
'split' => 'boolean',
'for_promise' => 'boolean',
];
public $fillable = ['name', 'fee_id'];
public function getAmount(): int
{

View File

@ -39,12 +39,9 @@ class SubscriptionController extends Controller
{
$subscriptionParams = $request->validate([
'name' => 'required|max:255',
'split' => 'present|boolean',
'fee_id' => 'required|exists:fees,id',
'for_promise' => 'present|boolean',
], [], [
'fee_id' => 'Nami-Beitrag',
'for_promise' => 'Für Versprechen benutzen',
]);
$children = $request->validate([
@ -76,12 +73,9 @@ class SubscriptionController extends Controller
{
$subscriptionParams = $request->validate([
'name' => 'required|max:255',
'split' => 'present|boolean',
'fee_id' => 'required|exists:fees,id',
'for_promise' => 'present|boolean',
], [], [
'fee_id' => 'Nami-Beitrag',
'for_promise' => 'Für Versprechen benutzen',
]);
$subscription->update($subscriptionParams);
$children = $request->validate([

View File

@ -28,9 +28,7 @@ class SubscriptionResource extends JsonResource
'fee_name' => $this->fee->name,
'amount_human' => number_format($this->getAmount() / 100, 2, ',', '.') . ' €',
'amount' => $this->getAmount(),
'split' => $this->split,
'children' => SubscriptionChildResource::collection($this->whenLoaded('children')),
'for_promise' => $this->for_promise,
];
}

View File

@ -20,7 +20,6 @@ class SubscriptionFactory extends Factory
return [
'name' => $this->faker->word,
'fee_id' => Fee::factory()->createOne()->id,
'for_promise' => false,
];
}
@ -42,9 +41,4 @@ class SubscriptionFactory extends Factory
return $instance;
}
public function forPromise(): self
{
return $this->state(['for_promise' => true]);
}
}

View File

@ -1,5 +1,6 @@
<?php
use App\Invoice\BillKind;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
@ -34,6 +35,68 @@ return new class extends Migration
$table->unsignedBigInteger('price');
$table->timestamps();
});
foreach (DB::table('subscriptions')->get() as $subscription) {
$children = DB::table('subscription_children')->where('parent_id', $subscription->id)->get();
if ($subscription->split === 1) {
foreach ($children as $child) {
$newName = 'Beitrag {year} für {name} (' . $child->name . ')';
DB::table('subscription_children')->where('id', $child->id)->update(['name' => $newName]);
}
} else {
DB::table('subscription_children')->where('parent_id', $subscription->id)->delete();
DB::table('subscription_children')->insert([
'id' => Str::uuid()->toString(),
'name' => 'Beitrag {year} für {name} (' . $subscription->name . ')',
'amount' => $children->sum('amount'),
'parent_id' => $subscription->id,
]);
}
}
$paymentGroups = DB::table('payments')->where('status_id', 2)->get()->groupBy(function ($payment) {
$member = DB::table('members')->where('id', $payment->member_id)->first();
return $member->lastname . $member->address . $member->location . $member->zip;
});
foreach ($paymentGroups as $payments) {
$member = DB::table('members')->where('id', $payments->first()->member_id)->first();
$invoiceId = DB::table('invoices')->insertGetId([
'to' => json_encode([
'name' => 'Familie ' . $member->lastname,
'address' => $member->address,
'zip' => $member->zip,
'location' => $member->location,
]),
'greeting' => 'Liebe Familie ' . $member->lastname,
'status' => 'Rechnung gestellt',
'via' => BillKind::fromValue($member->bill_kind)->value,
'usage' => 'Mitgliedsbeitrag für ' . $member->lastname,
'mail_email' => $member->email_parents ?: $member->email,
'last_remembered_at' => $payments->first()->last_remembered_at,
'sent_at' => $payments->first()->last_remembered_at,
]);
foreach ($payments as $payment) {
$subscription = DB::table('subscriptions')->where('id', $payment->subscription_id)->first();
$subscriptionChildren = DB::table('subscription_children')->where('parent_id', $subscription->id)->get();
$paymentMember = DB::table('members')->where('id', $payment->member_id)->first();
foreach ($subscriptionChildren as $child) {
DB::table('invoice_positions')->insert([
'invoice_id' => $invoiceId,
'description' => str($child->name)->replace('{name}', $paymentMember->firstname . ' ' . $paymentMember->lastname)->replace('{year}', $payment->nr),
'price' => $child->amount,
'member_id' => $member->id,
]);
}
}
}
Schema::dropIfExists('payments');
Schema::table('subscriptions', function (Blueprint $table) {
$table->dropColumn('split');
$table->dropColumn('for_promise');
});
}
/**
@ -43,7 +106,17 @@ return new class extends Migration
*/
public function down()
{
Schema::table('subscriptions', function (Blueprint $table) {
$table->boolean('split')->default(false);
$table->boolean('for_promise')->default(false);
});
Schema::dropIfExists('invoice_positions');
Schema::dropIfExists('invoices');
Schema::create('payments', function ($table) {
$table->id();
$table->string('nr');
$table->integer('subscription_id');
$table->json('invoice_data');
});
}
};

View File

@ -10,21 +10,23 @@
<ui-box heading="Beitrag">
<div class="grid gap-4 sm:grid-cols-2">
<f-text id="name" v-model="inner.name" label="Name" size="sm" required></f-text>
<f-select id="fee_id" name="fee_id" :options="fees" v-model="inner.fee_id" label="Nami-Beitrag" size="sm" required></f-select>
<f-switch id="split" label="Rechnung aufsplitten" v-model="inner.split" size="sm"></f-switch>
<f-switch id="for_promise" label="Für Versprechen benutzen" v-model="inner.for_promise" size="sm"></f-switch>
<f-select id="fee_id" v-model="inner.fee_id" name="fee_id" :options="fees" label="Nami-Beitrag"
size="sm" required></f-select>
</div>
</ui-box>
<ui-box heading="Positionen">
<div class="flex flex-col space-y-4">
<div v-for="(pos, index) in inner.children" :key="index" class="flex space-x-2 items-end">
<f-text :id="`name-${index}`" v-model="pos.name" label="Name" size="sm" required></f-text>
<f-text :id="`amount-${index}`" v-model="pos.amount" label="Beitrag" size="sm" mode="area" required></f-text>
<a href="#" @click.prevent="inner.children.splice(index, 1)" class="btn btn-sm btn-danger icon flex-none">
<f-text :id="`amount-${index}`" v-model="pos.amount" label="Beitrag" size="sm" mode="area"
required></f-text>
<a href="#" class="btn btn-sm btn-danger icon flex-none"
@click.prevent="inner.children.splice(index, 1)">
<ui-sprite src="trash" class="w-5 h-5"></ui-sprite>
</a>
</div>
<a href="#" @click.prevent="inner.children.push({name: '', amount: 0})" class="btn btn-sm flex btn-primary flex self-start mt-4">
<a href="#" class="btn btn-sm flex btn-primary flex self-start mt-4"
@click.prevent="inner.children.push({ name: '', amount: 0 })">
<ui-sprite src="plus" class="w-5 h-5"></ui-sprite>
Position hinzufügen
</a>
@ -36,11 +38,6 @@
<script>
export default {
data: function () {
return {
inner: {...this.data},
};
},
props: {
data: {},
@ -48,6 +45,11 @@ export default {
mode: {},
meta: {},
},
data: function () {
return {
inner: { ...this.data },
};
},
methods: {
submit() {

View File

@ -126,8 +126,6 @@ class PullMemberActionTest extends TestCase
$this->assertDatabaseHas('subscriptions', [
'fee_id' => $fee->id,
'name' => 'Lala',
'split' => false,
'for_promise' => false,
]);
$this->assertDatabaseHas('subscription_children', [
'name' => 'Lala',

View File

@ -15,10 +15,10 @@ class EditTest extends TestCase
public function testItReturnsChildren(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
$subscription = Subscription::factory()->name('hi')->forPromise()->for(Fee::factory())->children([
$subscription = Subscription::factory()->name('hi')->for(Fee::factory())->children([
new Child('a', 1400),
new Child('b', 1500),
])->create(['split' => true]);
])->create();
$response = $this->get("/subscription/{$subscription->id}/edit");
@ -29,8 +29,6 @@ class EditTest extends TestCase
],
'name' => 'hi',
'id' => $subscription->id,
'split' => true,
'for_promise' => true,
], $response, 'data');
}
}

View File

@ -22,7 +22,7 @@ class StoreTest extends TestCase
'/subscription',
SubscriptionRequestFactory::new()->fee($fee)->name('lorem')->children([
new Child('ch', 2500),
])->create(['split' => true, 'for_promise' => true])
])->create()
);
$response->assertRedirect('/subscription');
@ -30,8 +30,6 @@ class StoreTest extends TestCase
$this->assertDatabaseHas('subscriptions', [
'fee_id' => $fee->id,
'name' => 'lorem',
'split' => true,
'for_promise' => true,
]);
$this->assertDatabaseHas('subscription_children', [
'name' => 'ch',
@ -43,7 +41,7 @@ class StoreTest extends TestCase
public function testItValidatesSubscription(): void
{
$this->login()->loginNami();
$fee = Fee::factory()->create();
Fee::factory()->create();
$response = $this->post(
'/subscription',
@ -53,7 +51,6 @@ class StoreTest extends TestCase
$this->assertErrors([
'fee_id' => 'Nami-Beitrag ist nicht vorhanden.',
'name' => 'Name ist erforderlich.',
'for_promise' => 'Für Versprechen benutzen muss ein Wahrheitswert sein.',
], $response);
}
}

View File

@ -16,12 +16,12 @@ class UpdateTest extends TestCase
public function testItUpdatesASubscription(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
$subscription = Subscription::factory()->name('hi')->for(Fee::factory())->create(['split' => true, 'for_promise' => false]);
$subscription = Subscription::factory()->name('hi')->for(Fee::factory())->create();
$fee = Fee::factory()->create();
$response = $this->from("/subscription/{$subscription->id}")->patch(
"/subscription/{$subscription->id}",
SubscriptionRequestFactory::new()->amount(2500)->fee($fee)->name('lorem')->create(['split' => false, 'for_promise' => true])
SubscriptionRequestFactory::new()->amount(2500)->fee($fee)->name('lorem')->create()
);
$response->assertRedirect('/subscription');
@ -29,8 +29,6 @@ class UpdateTest extends TestCase
'id' => $subscription->id,
'fee_id' => $fee->id,
'name' => 'Lorem',
'split' => false,
'for_promise' => true,
]);
}
@ -71,7 +69,6 @@ class UpdateTest extends TestCase
$this->assertErrors([
'fee_id' => 'Nami-Beitrag ist nicht vorhanden.',
'name' => 'Name ist erforderlich.',
'for_promise' => 'Für Versprechen benutzen muss ein Wahrheitswert sein.',
], $response);
}
}

View File

@ -8,16 +8,14 @@ use Worksome\RequestFactories\RequestFactory;
class SubscriptionRequestFactory extends RequestFactory
{
/**
* @return array{fee_id: int, name: string, split: bool, for_promise: bool, children: array<int, array{amount: int, name: string}>}
* @return array{fee_id: int, name: string, children: array<int, array{amount: int, name: string}>}
*/
public function definition(): array
{
return [
'fee_id' => Fee::factory()->create()->id,
'name' => $this->faker->words(5, true),
'split' => $this->faker->boolean(),
'children' => [],
'for_promise' => $this->faker->boolean(),
];
}
@ -41,7 +39,6 @@ class SubscriptionRequestFactory extends RequestFactory
return $this->state([
'fee_id' => 9999,
'name' => '',
'for_promise' => 'A',
]);
}