Extend incoming for better usage.

This commit is contained in:
2025-02-07 14:21:59 +01:00
parent 643e68e9a7
commit aaefe7dc51
6 changed files with 147 additions and 16 deletions

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Supplier;
use Illuminate\Http\JsonResponse;
class SupplierController extends Controller
{
public function index(): JsonResponse
{
$suppliers = Supplier::orderBy('name')->get();
return response()->json($suppliers);
}
}

View File

@@ -22,6 +22,19 @@ class Incomingtax extends Model
'currency',
];
/**
* The attributes that are appended with attribute getters.
*
* @var string[]
*/
protected $appends = [
'gross',
];
public function getGrossAttribute() {
return number_format($this->taxable_amount + $this->amount, 2);
}
/**
* Get the incoming invoice this tax belongs to.
*/

View File

@@ -23,5 +23,7 @@ return [
'Existing invoices' => 'Bestehende Eingangsrechnungen',
'Edit incoming' => 'Bestehende Eingangsrechnungen bearbeiten',
'Incoming data' => 'Daten der Eingangsrechnung',
'Select supplier' => 'Lieferant wählen',
'Create incoming' => 'Neue Eingangsrechnung anlegen',
];

View File

@@ -1,7 +1,7 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('incoming.Edit incoming') }}
{{ __('incoming.Create incoming') }}
</h2>
</x-slot>
@@ -105,8 +105,18 @@
<form class="mt-6 space-y-6" @submit.prevent="">
<div class="flex flex-row">
<x-input-label class="w-1/6" for="select_supplier" :value="__('incoming.Select supplier')"/>
<select id="select_supplier" name="select_supplier" type="text" x-on:click="setSupplier();"
class="border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
<template x-for="supplier in suppliers">
<option :value="supplier.id" x-text="supplier.name"></option>
</template>
</select>
</div>
<div class="flex flex-row space-x-8 items-start">
<div class="w-1/2 grid grid-cols-3 items-center">
<div class="w-1/2 grid grid-cols-3 items-center">
<x-input-label for="name" :value="__('common.Name')"/>
<x-text-input id="name" name="name" type="text"
class="mt-1 col-span-2" :value="old('name')" autofocus
@@ -201,6 +211,7 @@
</header>
<div class="flex flex-row items-end gap-2 w-full">
<x-input-label for="invoice_item.gross" :value="__('invoice.Gross')" class="w-1/6"/>
<x-input-label for="invoice_item.amount" :value="__('invoice.Net')" class="w-1/6"/>
<x-input-label for="invoice_item.price" :value="__('configuration.Taxrate')" class="w-1/6"/>
<x-input-label for="invoice_item.tax" :value="__('common.Currency code')" class="w-1/6"/>
@@ -211,14 +222,19 @@
<div>
<template x-for="(tax, index) in taxes">
<div class="flex flex-row items-end gap-2 w-full relative">
<x-text-input id="taxes[index].gross" name="taxes[index].gross" type="number"
class="mt-1 block w-1/6"
autofocus x-on:blur="calculateFromGross(index);"
x-model="taxes[index].gross" />
<x-text-input id="taxes[index].taxable_amount" name="taxes[index].taxable_amount" type="number"
class="mt-1 block w-1/6"
autofocus x-on:blur="calculateTax(index);"
autofocus x-on:blur="calculateFromNet(index);"
x-model="taxes[index].taxable_amount" />
<x-text-input id="taxes[index].percentage" name="taxes[index].percentage" type="number"
class="mt-1 block w-1/6"
autofocus x-on:blur="calculateTax(index);"
autofocus x-on:blur="calculateFromNet(index);"
x-model="taxes[index].percentage"/>
<x-text-input id="taxes[index].currency" name="taxes[index].currency" type="text"
@@ -265,19 +281,46 @@
},
supplier: {},
taxes: [],
suppliers: [],
init() {
this.addTax();
this.getSuppliers();
},
addTax() {
let tax = {
gross: 0,
taxable_amount: 0,
amount: 0,
percentage: 0,
percentage: 19,
currency: this.incoming.currency_code
};
this.taxes.push(tax);
},
calculateTax(index) {
getSuppliers() {
let vm = this;
axios.get('/supplier')
.then(function(response) {
vm.suppliers = response.data;
})
},
setSupplier() {
let id = document.querySelector('#select_supplier').value;
let supplier_key = Object.keys(this.suppliers).find(key => (this.suppliers[key].id == id));
this.supplier = this.suppliers[supplier_key];
},
calculateFromGross(index) {
this.taxes[index].taxable_amount = this.taxes[index].gross * 100 / (100 + parseFloat(this.taxes[index].percentage));
this.calculateFromNet(index);
},
calculateFromNet(index) {
this.taxes[index].amount = this.taxes[index].taxable_amount * this.taxes[index].percentage / 100;
this.taxes[index].gross = this.taxes[index].taxable_amount * (100 + parseFloat(this.taxes[index].percentage)) / 100;
this.incoming.net = 0;
this.incoming.gross = 0;
this.incoming.tax = 0;

View File

@@ -105,6 +105,16 @@
<form class="mt-6 space-y-6" @submit.prevent="">
<div class="flex flex-row">
<x-input-label class="w-1/6" for="select_supplier" :value="__('incoming.Select supplier')"/>
<select id="select_supplier" name="select_supplier" type="text" x-on:click="setSupplier();"
class="border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
<template x-for="sup in suppliers">
<option :value="sup.id" :selected="sup.id == supplier.id" x-text="sup.name"></option>
</template>
</select>
</div>
<div class="flex flex-row space-x-8 items-start">
<div class="w-1/2 grid grid-cols-3 items-center">
<x-input-label for="name" :value="__('common.Name')"/>
@@ -273,7 +283,6 @@
</template>
</div>
</section>
</div>
</div>
@@ -290,29 +299,30 @@
</header>
<div class="flex flex-row items-end gap-2 w-full">
<x-input-label for="invoice_item.gross" :value="__('invoice.Gross')" class="w-1/6"/>
<x-input-label for="invoice_item.amount" :value="__('invoice.Net')" class="w-1/6"/>
<x-input-label for="invoice_item.name" :value="__('invoice.Tax')" class="w-1/6"/>
<x-input-label for="invoice_item.price" :value="__('configuration.Taxrate')" class="w-1/6"/>
<x-input-label for="invoice_item.tax" :value="__('common.Currency code')" class="w-1/6"/>
<x-input-label for="invoice_item.name" :value="__('invoice.Tax')" class="w-1/6"/>
<div class="w-1/12 relative h-10"></div>
</div>
<div>
<template x-for="(tax, index) in taxes">
<div class="flex flex-row items-end gap-2 w-full relative">
<x-text-input id="taxes[index].gross" name="taxes[index].gross" type="number"
class="mt-1 block w-1/6"
autofocus x-on:blur="calculateFromGross(index);"
x-model="taxes[index].gross"/>
<x-text-input id="taxes[index].taxable_amount" name="taxes[index].taxable_amount" type="number"
class="mt-1 block w-1/6"
autofocus
autofocus x-on:blur="calculateFromNet(index);"
x-model="taxes[index].taxable_amount"/>
<x-text-input id="taxes[index].amount" name="taxes[index].amount" type="number"
class="mt-1 block w-1/6"
autofocus
x-model="taxes[index].amount"/>
<x-text-input id="taxes[index].percentage" name="taxes[index].percentage" type="number"
class="mt-1 block w-1/6"
autofocus
autofocus x-on:blur="calculateFromNet(index);"
x-model="taxes[index].percentage"/>
<x-text-input id="taxes[index].currency" name="taxes[index].currency" type="text"
@@ -321,6 +331,11 @@
placeholder="{{ __('invoice.Name') }}"
x-model="taxes[index].currency"/>
<x-text-input id="taxes[index].amount" name="taxes[index].amount" type="number"
class="mt-1 block w-1/6"
autofocus disabled
x-model="taxes[index].amount"/>
<div class="flex flex-row w-1/12 h-10 relative">
<x-danger-button x-on:click="deleteTax(index);" class="absolute right-0">
-
@@ -347,6 +362,44 @@
supplier: {!! $supplier !!},
items: {!! $items !!},
taxes: {!! $taxes !!},
suppliers: [],
init() {
let vm = this;
axios.get('/supplier')
.then(function(response) {
vm.suppliers = response.data;
})
},
setSupplier() {
let id = document.querySelector('#select_supplier').value;
let supplier_key = Object.keys(this.suppliers).find(key => (this.suppliers[key].id == id));
this.supplier = this.suppliers[supplier_key];
},
calculateFromGross(index) {
this.taxes[index].taxable_amount = this.taxes[index].gross * 100 / (100 + parseFloat(this.taxes[index].percentage));
this.calculateFromNet(index);
},
calculateFromNet(index) {
this.taxes[index].amount = this.taxes[index].taxable_amount * this.taxes[index].percentage / 100;
this.taxes[index].gross = this.taxes[index].taxable_amount * (100 + parseFloat(this.taxes[index].percentage)) / 100;
this.incoming.net = 0;
this.incoming.gross = 0;
this.incoming.tax = 0;
this.calculateSum();
},
calculateSum() {
let vm = this;
this.taxes.forEach(function(tax) {
vm.incoming.tax += tax.amount * 1;
vm.incoming.net += tax.taxable_amount * 1;
});
vm.incoming.gross += (vm.incoming.tax + vm.incoming.net);
},
addItem() {
let item = {
@@ -366,9 +419,10 @@
addTax() {
let tax = {
incoming_id: this.incoming_id,
gross: 0,
taxable_amount: 0,
amount: 0,
percentage: 0,
percentage: 19,
currency: 'EUR'
};
this.taxes.push(tax);

View File

@@ -13,6 +13,7 @@ use App\Http\Controllers\Api\MailController;
use App\Http\Controllers\Api\OptionController;
use App\Http\Controllers\Api\PaymentController;
use App\Http\Controllers\Api\ProjectController;
use App\Http\Controllers\Api\SupplierController;
use App\Http\Controllers\Api\TaxrateController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
@@ -51,6 +52,7 @@ Route::group(['as' => 'api.'], function () {
Route::post('/incoming', [IncomingController::class, 'store'])->name('incoming.store');
Route::apiResource('/project', ProjectController::class);
Route::apiResource('/dashboard', DashboardController::class)->only(['index', 'update']);
Route::apiResource('/supplier', SupplierController::class)->only(['index']);
});