Build the stuff for incoming invoices.
This commit is contained in:
@@ -3,8 +3,10 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Incoming;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
@@ -31,7 +33,7 @@ class ExcelController extends Controller
|
||||
$requestData = $request->validate([
|
||||
'from' => 'required|date',
|
||||
'end' => 'required|date',
|
||||
'report' => 'required|in:invoice,payment',
|
||||
'report' => 'required|in:invoice,payment,incoming,outgoing',
|
||||
]);
|
||||
|
||||
$this->from = $requestData['from'];
|
||||
@@ -51,7 +53,14 @@ class ExcelController extends Controller
|
||||
case 'payment':
|
||||
$this->payment();
|
||||
break;
|
||||
case 'incoming':
|
||||
$this->incoming();
|
||||
break;
|
||||
case 'outgoing':
|
||||
$this->outgoing();
|
||||
break;
|
||||
}
|
||||
|
||||
return response()->json($this->writer->save('php://output'));
|
||||
}
|
||||
|
||||
@@ -159,6 +168,7 @@ class ExcelController extends Controller
|
||||
$row = 2;
|
||||
foreach ($payments as $payment) {
|
||||
$invoice = $payment->invoice;
|
||||
|
||||
foreach ($invoice->items as $item) {
|
||||
if (!isset($net[$item->tax])) {
|
||||
$net[$item->tax] = 0;
|
||||
@@ -169,6 +179,7 @@ class ExcelController extends Controller
|
||||
$tax[$item->tax] += $item->amount * $item->price * $item->tax * $payment->paid_amount / ($invoice->sum * 100);
|
||||
$gross[$item->tax] += $item->total * $payment->paid_amount / ($invoice->sum);
|
||||
}
|
||||
|
||||
$worksheet->setCellValue('A' . $row, $invoice->number);
|
||||
$worksheet->setCellValue('B' . $row, $invoice->customer->name);
|
||||
$worksheet->setCellValue('C' . $row, $invoice->address->name);
|
||||
@@ -204,6 +215,97 @@ class ExcelController extends Controller
|
||||
}
|
||||
|
||||
$this->writer = new Xlsx($spreadsheet);
|
||||
}
|
||||
|
||||
protected function incoming(): void
|
||||
{
|
||||
$incoming = Incoming::whereBetween('issue_date', [$this->from, $this->end])->with(['supplier', 'taxes'])->orderBy('issue_date', 'desc')->get();
|
||||
$this->incomingToSheet($incoming);
|
||||
}
|
||||
|
||||
protected function outgoing(): void
|
||||
{
|
||||
$incoming = Incoming::whereBetween('pay_date', [$this->from, $this->end])->with(['supplier', 'taxes'])->orderBy('pay_date', 'desc')->get();
|
||||
$this->incomingToSheet($incoming);
|
||||
}
|
||||
|
||||
protected function incomingToSheet(Collection $incoming): void
|
||||
{
|
||||
$net = [];
|
||||
$tax = [];
|
||||
$gross = [];
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
$worksheet->setTitle(__('incoming.Incoming'));
|
||||
$worksheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE);
|
||||
$worksheet->getHeaderFooter()->setEvenHeader(__('incoming.Invoices from to', ['from' => $this->from, 'end' => $this->end]));
|
||||
$worksheet->getHeaderFooter()->setOddHeader(__('incoming.Invoices from to', ['from' => $this->from, 'end' => $this->end]));
|
||||
|
||||
$worksheet->setCellValue('A1', __('invoice.Invoice Number'));
|
||||
$worksheet->setCellValue('B1', __('customer.Customer'));
|
||||
$worksheet->setCellValue('C1', __('common.Name'));
|
||||
$worksheet->setCellValue('D1', __('invoice.Net'));
|
||||
$worksheet->setCellValue('E1', __('invoice.Tax'));
|
||||
$worksheet->setCellValue('F1', __('invoice.Gross'));
|
||||
$worksheet->setCellValue('G1', __('common.Created at'));
|
||||
$worksheet->setCellValue('H1', __('common.Paid at'));
|
||||
$worksheet->getStyle('A1:H1')->getBorders()->getBottom()->applyFromArray(['borderStyle' => Border::BORDER_DOUBLE]);
|
||||
$worksheet->getStyle('A1:H1')->getFont()->setBold(true);
|
||||
$worksheet->getStyle('D1:H1')->getAlignment()->applyFromArray(['horizontal' => Alignment::HORIZONTAL_RIGHT]);
|
||||
|
||||
$row = 2;
|
||||
foreach ($incoming as $invoice) {
|
||||
foreach ($invoice->taxes as $item) {
|
||||
if (!isset($net[$item->percentage])) {
|
||||
$net[$item->percentage] = 0;
|
||||
$tax[$item->percentage] = 0;
|
||||
$gross[$item->percentage] = 0;
|
||||
}
|
||||
$net[$item->percentage] += $item->taxable_amount;
|
||||
$tax[$item->percentage] += $item->amount;
|
||||
$gross[$item->percentage] += $item->taxable_amount + $item->amount;
|
||||
}
|
||||
$worksheet->setCellValue('A' . $row, $invoice->invoice_number);
|
||||
$worksheet->setCellValue('B' . $row, $invoice->supplier->name);
|
||||
$worksheet->setCellValue('C' . $row, $invoice->supplier->email);
|
||||
$worksheet->getCell('D' . $row)->getStyle()->getNumberFormat()->setFormatCode($this->currencyMask);
|
||||
$worksheet->setCellValue('D' . $row, $invoice->net);
|
||||
$worksheet->getCell('E' . $row)->getStyle()->getNumberFormat()->setFormatCode($this->currencyMask);
|
||||
$worksheet->setCellValue('E' . $row, $invoice->tax);
|
||||
$worksheet->getCell('F' . $row)->getStyle()->getNumberFormat()->setFormatCode($this->currencyMask);
|
||||
$worksheet->setCellValue('F' . $row, $invoice->gross);
|
||||
$worksheet->setCellValue('G' . $row, Date::PHPToExcel(\DateTime::createFromFormat('!Y-m-d', $invoice->issue_date)));
|
||||
$worksheet->getCell('G' . $row)->getStyle()->getNumberFormat()->setFormatCode('dd.mm.yyyy');
|
||||
if (is_null($invoice->pay_date)) {
|
||||
$worksheet->setCellValue('H' . $row, '');
|
||||
} else {
|
||||
$worksheet->setCellValue('H' . $row, Date::PHPToExcel(\DateTime::createFromFormat('!Y-m-d', $invoice->pay_date)));
|
||||
}
|
||||
$worksheet->getCell('H' . $row)->getStyle()->getNumberFormat()->setFormatCode('dd.mm.yyyy');
|
||||
$row++;
|
||||
}
|
||||
|
||||
$worksheet->getStyle('A' . $row - 1 . ':H' . $row - 1)->getBorders()->getBottom()->applyFromArray(['borderStyle' => Border::BORDER_DOUBLE]);
|
||||
|
||||
foreach ($net as $tax_value => $amount) {
|
||||
$worksheet->getStyle('A' . $row . ':G' . $row)->getFont()->setBold(true);
|
||||
$worksheet->getStyle('D' . $row . ':G' . $row)->getAlignment()->applyFromArray(['horizontal' => Alignment::HORIZONTAL_RIGHT]);
|
||||
$worksheet->setCellValue('B' . $row, __('invoice.Sails from vat', ['tax' => \Illuminate\Support\Number::percentage($tax_value)]));
|
||||
$worksheet->getCell('D' . $row)->getStyle()->getNumberFormat()->setFormatCode($this->currencyMask);
|
||||
$worksheet->setCellValue('D' . $row, $amount);
|
||||
$worksheet->getCell('E' . $row)->getStyle()->getNumberFormat()->setFormatCode($this->currencyMask);
|
||||
$worksheet->setCellValue('E' . $row, $tax[$tax_value]);
|
||||
$worksheet->getCell('F' . $row)->getStyle()->getNumberFormat()->setFormatCode($this->currencyMask);
|
||||
$worksheet->setCellValue('F' . $row, $gross[$tax_value]);
|
||||
|
||||
$row++;
|
||||
}
|
||||
|
||||
foreach ($worksheet->getColumnIterator() as $column) {
|
||||
$worksheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true);
|
||||
}
|
||||
|
||||
$this->writer = new Xlsx($spreadsheet);
|
||||
}
|
||||
}
|
||||
|
||||
180
app/Http/Controllers/Api/IncomingController.php
Normal file
180
app/Http/Controllers/Api/IncomingController.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Incoming;
|
||||
use App\Models\Incomingitem;
|
||||
use App\Models\Incomingtax;
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class IncomingController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index($from = null, $end = null): JsonResponse
|
||||
{
|
||||
return response()->json(Incoming::whereBetween('issue_date', [$from, $end])->with(['supplier'])->orderBy('issue_date', 'desc')->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an upload file for the resource.
|
||||
*/
|
||||
public function upload(Request $request): JsonResponse
|
||||
{
|
||||
$xmlString = base64_decode(preg_replace('#^data:text/\w+;base64,#i', '', $request->uploadFile));
|
||||
$reader = new \App\XmlReader($xmlString);
|
||||
$supplierData = $reader->supplier();
|
||||
$incomingData = $reader->incoming();
|
||||
$itemsData = $reader->items();
|
||||
$taxesData = $reader->taxes();
|
||||
|
||||
$supplier = Supplier::where('email', '=', $supplierData['email'])->firstOrNew($supplierData);
|
||||
$supplier->save();
|
||||
|
||||
$incomingData['supplier_id'] = $supplier->id;
|
||||
|
||||
$incoming = new Incoming($incomingData);
|
||||
$incoming->save();
|
||||
|
||||
foreach ($itemsData as $item) {
|
||||
$item['incoming_id'] = $incoming->id;
|
||||
$incomingItem = new Incomingitem($item);
|
||||
$incomingItem->save();
|
||||
}
|
||||
|
||||
foreach ($taxesData as $tax) {
|
||||
$tax['incoming_id'] = $incoming->id;
|
||||
$incomingTax = new Incomingtax($tax);
|
||||
$incomingTax->save();
|
||||
}
|
||||
|
||||
return response()->json($incoming);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$supplierData = $request->validate([
|
||||
'supplier.name' => 'required|string',
|
||||
'supplier.registration_name' => 'nullable|string',
|
||||
'supplier.email' => 'nullable|email',
|
||||
'supplier.address' => 'nullable|string',
|
||||
'supplier.city' => 'nullable|string',
|
||||
'supplier.zip' => 'nullable|string',
|
||||
'supplier.country_code' => 'nullable|string',
|
||||
'supplier.tax_fc' => 'nullable|string',
|
||||
'supplier.tax_vat' => 'nullable|string',
|
||||
'supplier.contact_name' => 'nullable|string',
|
||||
'supplier.contact_phone' => 'nullable|string',
|
||||
'supplier.contact_email' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$supplier = Supplier::where('email', '=', $supplierData['supplier']['email'])->firstOrNew($supplierData['supplier']);
|
||||
$supplier->save();
|
||||
|
||||
$incomingData = $request->validate([
|
||||
'incoming.invoice_number' => 'required|string',
|
||||
'incoming.issue_date' => 'required|date',
|
||||
'incoming.due_date' => 'nullable|date',
|
||||
'incoming.invoice_type_code' => 'required|string',
|
||||
'incoming.currency_code' => 'required|string',
|
||||
'incoming.net' => 'required|numeric',
|
||||
'incoming.gross' => 'required|numeric',
|
||||
'incoming.tax' => 'required|numeric',
|
||||
'incoming.pay_date' => 'nullable|date',
|
||||
'incoming.pay_name' => 'nullable|string',
|
||||
'incoming.pay_bic' => 'nullable|string',
|
||||
'incoming.pay_iban' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$incomingData['incoming']['supplier_id'] = $supplier->id;
|
||||
$incoming = new Incoming($incomingData['incoming']);
|
||||
$incoming->save();
|
||||
|
||||
$taxesData = $request->validate([
|
||||
'taxes.*.taxable_amount' => 'required|numeric',
|
||||
'taxes.*.amount' => 'required|numeric',
|
||||
'taxes.*.percentage' => 'required|numeric',
|
||||
'taxes.*.currency' => 'required|string',
|
||||
]);
|
||||
|
||||
$incoming->taxes()->createMany($taxesData['taxes']);
|
||||
|
||||
return response()->json($supplierData['supplier']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Incoming $incoming): JsonResponse
|
||||
{
|
||||
$incomingData = $request->validate([
|
||||
'incoming.invoice_number' => 'required|string',
|
||||
'incoming.issue_date' => 'required|date',
|
||||
'incoming.due_date' => 'nullable|date',
|
||||
'incoming.invoice_type_code' => 'required|string',
|
||||
'incoming.currency_code' => 'required|string',
|
||||
'incoming.net' => 'required|numeric',
|
||||
'incoming.gross' => 'required|numeric',
|
||||
'incoming.tax' => 'required|numeric',
|
||||
'incoming.pay_date' => 'nullable|date',
|
||||
'incoming.pay_name' => 'nullable|string',
|
||||
'incoming.pay_bic' => 'nullable|string',
|
||||
'incoming.pay_iban' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$incoming->update($incomingData['incoming']);
|
||||
|
||||
$supplierData = $request->validate([
|
||||
'supplier.name' => 'required|string',
|
||||
'supplier.registration_name' => 'nullable|string',
|
||||
'supplier.email' => 'nullable|email',
|
||||
'supplier.address' => 'nullable|string',
|
||||
'supplier.city' => 'nullable|string',
|
||||
'supplier.zip' => 'nullable|string',
|
||||
'supplier.country_code' => 'nullable|string',
|
||||
'supplier.tax_fc' => 'nullable|string',
|
||||
'supplier.tax_vat' => 'nullable|string',
|
||||
'supplier.contact_name' => 'nullable|string',
|
||||
'supplier.contact_phone' => 'nullable|string',
|
||||
'supplier.contact_email' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$incoming->supplier()->update($supplierData['supplier']);
|
||||
|
||||
$itemsData = $request->validate([
|
||||
'items.*.name' => 'required|string',
|
||||
'items.*.article_number' => 'nullable|string',
|
||||
'items.*.description' => 'nullable|string',
|
||||
'items.*.amount' => 'required|numeric',
|
||||
'items.*.discount' => 'nullable|numeric',
|
||||
'items.*.tax' => 'required|numeric',
|
||||
'items.*.price' => 'required|numeric',
|
||||
'items.*.total' => 'required|numeric',
|
||||
]);
|
||||
|
||||
$incoming->items()->delete();
|
||||
|
||||
if (!empty($itemsData)) {
|
||||
$incoming->items()->createMany($itemsData['items']);
|
||||
}
|
||||
|
||||
$taxesData = $request->validate([
|
||||
'taxes.*.taxable_amount' => 'required|numeric',
|
||||
'taxes.*.amount' => 'required|numeric',
|
||||
'taxes.*.percentage' => 'required|numeric',
|
||||
'taxes.*.currency' => 'required|string',
|
||||
]);
|
||||
|
||||
$incoming->taxes()->delete();
|
||||
$incoming->taxes()->createMany($taxesData['taxes']);
|
||||
|
||||
return response()->json($incoming);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ class InvoiceController extends Controller
|
||||
return response()->json(Invoice::whereBetween('created_at', [$from, $end])->with(['address', 'customer'])->orderBy('created_at', 'desc')->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource that have a status "sent".
|
||||
*/
|
||||
public function open(): JsonResponse
|
||||
{
|
||||
return response()->json(Invoice::where('status', '=', 'sent')->orderBy('created_at', 'desc')->with(['customer', 'address', 'payments'])->get());
|
||||
@@ -49,14 +52,6 @@ class InvoiceController extends Controller
|
||||
return response()->json($invoice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Invoice $invoice)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
@@ -76,6 +71,9 @@ class InvoiceController extends Controller
|
||||
return response()->json($invoice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource's status in storage.
|
||||
*/
|
||||
public function state(Request $request, Invoice $invoice): JsonResponse
|
||||
{
|
||||
$invoiceData = $request->validate([
|
||||
|
||||
41
app/Http/Controllers/IncomingController.php
Normal file
41
app/Http/Controllers/IncomingController.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Incoming;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
class IncomingController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
return view('incoming.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('incoming.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for uploading a new resource.
|
||||
*/
|
||||
public function upload(): View
|
||||
{
|
||||
return view('incoming.upload');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Incoming $incoming): View
|
||||
{
|
||||
return view('incoming.edit', ['incoming' => $incoming, 'supplier' => $incoming->supplier, 'items' => $incoming->items, 'taxes' => $incoming->taxes]);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
@@ -31,6 +32,41 @@ class Incoming extends Model
|
||||
'pay_iban',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are appended with attribute getters.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $appends = [
|
||||
'created',
|
||||
'paid',
|
||||
'due',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the pay_date attribute in local time format.
|
||||
*/
|
||||
public function getDueAttribute(): string
|
||||
{
|
||||
return (is_null($this->due_date)) ? '' : Carbon::createFromFormat('Y-m-d', $this->due_date)->format('d.m.Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pay_date attribute in local time format.
|
||||
*/
|
||||
public function getPaidAttribute(): string
|
||||
{
|
||||
return (is_null($this->pay_date)) ? '' : Carbon::createFromFormat('Y-m-d', $this->pay_date)->format('d.m.Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the issue_date attribute in local time format.
|
||||
*/
|
||||
public function getCreatedAttribute(): string
|
||||
{
|
||||
return Carbon::createFromFormat('Y-m-d', $this->issue_date)->format('d.m.Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the supplier of the incoming invoice.
|
||||
*/
|
||||
|
||||
99
app/XmlReader.php
Normal file
99
app/XmlReader.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use \DOMDocument;
|
||||
use \DOMXPath;
|
||||
|
||||
class XmlReader
|
||||
{
|
||||
private DOMDocument $xml;
|
||||
private DOMXPath $xpath;
|
||||
|
||||
public function __construct(string $xml)
|
||||
{
|
||||
$this->xml = new DOMDocument();
|
||||
$this->xml->loadXML($xml);
|
||||
$this->xpath = new DOMXPath($this->xml);
|
||||
}
|
||||
|
||||
public function incoming(): array
|
||||
{
|
||||
$incoming = [];
|
||||
|
||||
$incoming['invoice_number'] = $this->xpath->query('cbc:ID', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['issue_date'] = $this->xpath->query('cbc:IssueDate', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['due_date'] = $this->xpath->query('cbc:DueDate', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['invoice_type_code'] = $this->xpath->query('cbc:InvoiceTypeCode', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['currency_code'] = $this->xpath->query('cbc:DocumentCurrencyCode', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['net'] = $this->xpath->query('cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['gross'] = $this->xpath->query('cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['tax'] = $this->xpath->query('cac:TaxTotal/cbc:TaxAmount', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['pay_name'] = $this->xpath->query('cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:Name', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
$incoming['pay_bic'] = ($this->xpath->query('cac:PaymentMeans/cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID', $this->xml->documentElement)->length > 0) ? $this->xpath->query('cac:PaymentMeans/cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID', $this->xml->documentElement)->item(0)->nodeValue : null;
|
||||
$incoming['pay_iban'] = $this->xpath->query('cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID', $this->xml->documentElement)->item(0)->nodeValue;
|
||||
|
||||
return $incoming;
|
||||
}
|
||||
|
||||
public function supplier(): array
|
||||
{
|
||||
$supplier = [];
|
||||
$supplierNode = $this->xpath->query('cac:AccountingSupplierParty', $this->xml->documentElement)->item(0);
|
||||
|
||||
$supplier['name'] = $this->xpath->query("cac:Party/cac:PartyName/cbc:Name", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['registration_name'] = $this->xpath->query("cac:Party/cac:PartyLegalEntity/cbc:RegistrationName", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['email'] = $this->xpath->query("cac:Party/cbc:EndpointID", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['address'] = $this->xpath->query("cac:Party/cac:PostalAddress/cbc:StreetName", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['city'] = $this->xpath->query("cac:Party/cac:PostalAddress/cbc:CityName", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['zip'] = $this->xpath->query("cac:Party/cac:PostalAddress/cbc:PostalZone", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['country_code'] = $this->xpath->query("cac:Party/cac:PostalAddress/cac:Country/cbc:IdentificationCode", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['tax_fc'] = ($this->xpath->query("cac:Party/cac:PartyTaxScheme[cac:TaxScheme/cbc:ID/text() = 'FC']", $supplierNode)->length === 0) ? null : $this->xpath->query("cac:Party/cac:PartyTaxScheme[cac:TaxScheme/cbc:ID/text() = 'FC']/cbc:CompanyID", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['tax_vat'] = ($this->xpath->query("cac:Party/cac:PartyTaxScheme[cac:TaxScheme/cbc:ID/text() = 'VAT']", $supplierNode)->length === 0) ? null : $this->xpath->query("cac:Party/cac:PartyTaxScheme[cac:TaxScheme/cbc:ID/text() = 'VAT']/cbc:CompanyID", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['contact_name'] = $this->xpath->query("cac:Party/cac:Contact/cbc:Name", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['contact_phone'] = $this->xpath->query("cac:Party/cac:Contact/cbc:Telephone", $supplierNode)->item(0)->nodeValue;
|
||||
$supplier['contact_email'] = $this->xpath->query("cac:Party/cac:Contact/cbc:ElectronicMail", $supplierNode)->item(0)->nodeValue;
|
||||
|
||||
return $supplier;
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
$items = [];
|
||||
$itemList = $this->xpath->query('cac:InvoiceLine', $this->xml->documentElement);
|
||||
|
||||
foreach ($itemList as $itemNode) {
|
||||
$item = [];
|
||||
$item['name'] = $this->xpath->query("cac:Item/cbc:Name", $itemNode)->item(0)->nodeValue;
|
||||
$item['article_number'] = null;
|
||||
$item['description'] = ($this->xpath->query("cac:Item/cbc:Description", $itemNode)->length === 0) ? null : $this->xpath->query("cac:Item/cbc:Description", $itemNode)->item(0)->nodeValue;
|
||||
$item['amount'] = $this->xpath->query("cbc:InvoicedQuantity", $itemNode)->item(0)->nodeValue;
|
||||
$item['discount'] = null;
|
||||
$item['tax'] = $this->xpath->query("cac:Item/cac:ClassifiedTaxCategory/cbc:Percent", $itemNode)->item(0)->nodeValue;
|
||||
$item['price'] = $this->xpath->query("cac:Price/cbc:PriceAmount", $itemNode)->item(0)->nodeValue;
|
||||
$item['total'] = $this->xpath->query("cbc:LineExtensionAmount", $itemNode)->item(0)->nodeValue;
|
||||
|
||||
$items[] = $item;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function taxes(): array
|
||||
{
|
||||
$taxes = [];
|
||||
$taxList = $this->xpath->query('cac:TaxTotal/cac:TaxSubtotal', $this->xml->documentElement);
|
||||
|
||||
foreach ($taxList as $taxNode) {
|
||||
$tax = [];
|
||||
$tax['taxable_amount'] = $this->xpath->query("cbc:TaxableAmount", $taxNode)->item(0)->nodeValue;
|
||||
$tax['amount'] = $this->xpath->query("cbc:TaxAmount", $taxNode)->item(0)->nodeValue;
|
||||
$tax['percentage'] = $this->xpath->query("cac:TaxCategory/cbc:Percent", $taxNode)->item(0)->nodeValue;
|
||||
$tax['currency'] = $this->xpath->query("cbc:TaxableAmount", $taxNode)->item(0)->getAttribute("currencyID");
|
||||
|
||||
$taxes[] = $tax;
|
||||
}
|
||||
|
||||
return $taxes;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@
|
||||
"laravel/framework": "^11.31",
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/tinker": "^2.9",
|
||||
"phpoffice/phpspreadsheet": "^3.8"
|
||||
"phpoffice/phpspreadsheet": "^3.8",
|
||||
"ext-dom": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
|
||||
@@ -26,6 +26,7 @@ return [
|
||||
'Zip Code' => 'Postleitzahl',
|
||||
'City' => 'Stadt',
|
||||
'Created at' => 'Erstellt am',
|
||||
'Paid at' => 'Bezahlt am',
|
||||
'Search' => 'Suchen',
|
||||
'You\'re logged in!' => 'Du bist angemeldet!',
|
||||
'Confirm' => 'Bestätigen',
|
||||
@@ -38,5 +39,13 @@ return [
|
||||
'Email-Subject' => 'Betreff',
|
||||
'Email-Body' => 'Nachricht',
|
||||
'Tooltip multiple email' => 'Mehrere E-Mail-Adressen werden durch "," (Komma) von einander getrennt.',
|
||||
'Registration name' => 'Registrierungsname',
|
||||
'Country code' => 'Ländercode',
|
||||
'Tax FC' => 'Steuernummer',
|
||||
'Tax VAT' => 'USt-Id',
|
||||
'Contact name' => 'Ansprechpartner',
|
||||
'Contact email' => 'E-Mail',
|
||||
'Contact phone' => 'Telefon',
|
||||
'Currency code' => 'Währungscode',
|
||||
|
||||
];
|
||||
|
||||
@@ -33,6 +33,7 @@ return [
|
||||
'Are you sure you want to delete the address?' => 'Sicher, dass die Adresse gelöscht werden soll?',
|
||||
'Once the address is deleted, all the ressources and data will be permanently deleted.' => 'Sobald die Adresse gelöscht wird, werden alle Ressourcen und Daten dauerhaft gelöscht.',
|
||||
'Enter your customer\'s address.' => 'Gib die Adresse des Kunden ein.',
|
||||
'Hint edit address.' => 'Bei bestehenden Adressen kann nur gewählt werden, ob es sich um eine Rechnungs- oder Versandadresse handelt. Die Bearbeitung der anderen Felder ist unterbunden, da diese Daten Einfluss auf bereits bestehende Rechnungen haben.'
|
||||
'Hint edit address.' => 'Bei bestehenden Adressen kann nur gewählt werden, ob es sich um eine Rechnungs- oder Versandadresse handelt. Die Bearbeitung der anderen Felder ist unterbunden, da diese Daten Einfluss auf bereits bestehende Rechnungen haben.',
|
||||
'Edit address' => 'Adresse bearbeiten',
|
||||
|
||||
];
|
||||
|
||||
@@ -16,5 +16,7 @@ return [
|
||||
'Customers without address' => 'Kunden ohne Rechnungsadresse',
|
||||
'Invoices not sent' => 'Nicht versendete Rechnungen',
|
||||
'Invoices not paid' => 'Nicht bezahlte Rechnungen',
|
||||
'Incoming not paid' => 'Nicht bezahlte Eingangsrechnungen',
|
||||
'Incoming paid' => 'Bezahlte Eingangsrechnungen',
|
||||
|
||||
];
|
||||
|
||||
@@ -19,6 +19,8 @@ return [
|
||||
'Generate' => 'Generieren',
|
||||
'Outgoing invoices' => 'Ausgangsrechnungen',
|
||||
'Incoming payments' => 'Zahlungseingang',
|
||||
'Incoming by issue date' => 'Eingang nach Rechnungsdatum',
|
||||
'Outgoing by pay date' => 'Eingang nach Zahlungsdatum',
|
||||
|
||||
|
||||
];
|
||||
|
||||
@@ -20,5 +20,6 @@ return [
|
||||
'Saved' => 'Gespeichert',
|
||||
'Cancel' => 'Abbrechen',
|
||||
'Send' => 'Senden',
|
||||
'Upload' => 'Hochladen',
|
||||
|
||||
];
|
||||
|
||||
27
lang/de/incoming.php
Normal file
27
lang/de/incoming.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|------------------------------------------------- -------------------------
|
||||
| Übersetzungen der Eingangsrechnungen
|
||||
|------------------------------------------------- -------------------------
|
||||
|
|
||||
| Die folgenden Sprachzeilen werden bei den Eingangsrechlnungen verwendet.
|
||||
|
|
||||
*/
|
||||
|
||||
'Incoming' => 'Eingang',
|
||||
'Add new invoice' => 'Neue Eingangsrechnung manuell anlegen',
|
||||
'Add new invoice by clicking add' => 'Neue Eingangsrechnung durch Klick auf "Anlegen" erstellen. Die Daten zur Rechnung müssen händisch eingegeben werden.',
|
||||
'Upload new invoice' => 'Neue Eingangsrechnung hochladen',
|
||||
'Upload new xml invoice by clicking upload' => 'Neue Eingangsrechnung durch Klick auf "Hochladen" erstellen. Es können nur XML Rechnungen im e-Rechnungsformat verarbeitet werden.',
|
||||
'Invoice file' => 'Rechnungsdatei',
|
||||
'Invoices from to' => 'Eingangsrechnungen vom :from bis :end',
|
||||
'Payments from to' => 'Zahlungsausgänge vom :from bis :end',
|
||||
'Outgoing' => 'Bezahlte Eingangsrechnungen',
|
||||
'Existing invoices' => 'Bestehende Eingangsrechnungen',
|
||||
'Edit incoming' => 'Bestehende Eingangsrechnungen bearbeiten',
|
||||
'Incoming data' => 'Daten der Eingangsrechnung',
|
||||
|
||||
];
|
||||
@@ -35,6 +35,8 @@ return [
|
||||
'State' => 'Status',
|
||||
'Invoice Number' => 'Rechnungsnummer',
|
||||
'Invoice Number short' => 'Rechnung-Nr.',
|
||||
'Invoice Date' => 'Rechnungsdatum',
|
||||
'Invoice Due date' => 'Fälligkeitsdatum',
|
||||
'Sum' => 'Summe',
|
||||
'Net' => 'Nettobetrag',
|
||||
'Gross' => 'Gesamtbetrag',
|
||||
@@ -81,5 +83,10 @@ return [
|
||||
'Sails from vat' => 'Umsätze aus :tax Mehrwertsteuer',
|
||||
'Invoices from to' => 'Rechnungen vom :from bis :end',
|
||||
'Payments from to' => 'Zahlungseingänge vom :from bis :end',
|
||||
'Account holder' => 'Kontoinhaber',
|
||||
'Tax items' => 'Steueranteile',
|
||||
'Article number' => 'Artikelnummer',
|
||||
'Discount' => 'Rabatt',
|
||||
'Type code' => 'Rechnungstyp',
|
||||
|
||||
];
|
||||
|
||||
16
lang/de/supplier.php
Normal file
16
lang/de/supplier.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|------------------------------------------------- -------------------------
|
||||
| Übersetzungen der Lieferanten
|
||||
|------------------------------------------------- -------------------------
|
||||
|
|
||||
| Die folgenden Sprachzeilen werden bei den Lieferanten verwendet.
|
||||
|
|
||||
*/
|
||||
|
||||
'Supplier' => 'Lieferant',
|
||||
|
||||
];
|
||||
@@ -2,7 +2,7 @@
|
||||
$customers = \App\Models\Customer::doesntHave('address')->get();
|
||||
$created_invoices = \App\Models\Invoice::where('status', '=', 'created')->orderBy('created_at')->get();
|
||||
$sent_invoices = \App\Models\Invoice::where('status', '=', 'sent')->orderBy('created_at')->get();
|
||||
|
||||
$unpaid_incoming = \App\Models\Incoming::where('pay_date', '=', null)->orderBy('due_date')->get();
|
||||
@endphp
|
||||
|
||||
<x-app-layout>
|
||||
@@ -24,7 +24,7 @@
|
||||
class="flex max-w even:bg-gray-100 odd:bg-white">
|
||||
<div class="w-1/4">{{ $invoice->number }}</div>
|
||||
<div class="w-1/4">{{ $invoice->address->name }}</div>
|
||||
<div class="w-1/4">{{ $invoice->sum }}</div>
|
||||
<div class="w-1/4">{{ \Illuminate\Support\Number::currency($invoice->sum) }}</div>
|
||||
<div class="w-1/4">{{ $invoice->created }}</div>
|
||||
</a>
|
||||
@endforeach
|
||||
@@ -59,6 +59,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Incoming invoices, that are not paid -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg col-span-2">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Incoming not paid') }}</h2>
|
||||
@foreach($unpaid_incoming as $incoming)
|
||||
<a href="{{ route('incoming.edit', $incoming->id) }}"
|
||||
class="flex max-w even:bg-gray-100 odd:bg-white">
|
||||
<div class="w-1/6">{{ $incoming->invoice_number }}</div>
|
||||
<div class="w-1/2">{{ $incoming->supplier->name }}</div>
|
||||
<div class="w-1/6">{{ \Illuminate\Support\Number::currency($incoming->gross) }}</div>
|
||||
<div class="w-1/6">{{ $incoming->due }}</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
<select name="report" id="report" x-model="data.report" 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 w-1/4">
|
||||
<option value="invoice">{{ __('excel.Outgoing invoices') }}</option>
|
||||
<option value="payment">{{ __('excel.Incoming payments') }}</option>
|
||||
<option value="incoming">{{ __('excel.Incoming by issue date') }}</option>
|
||||
<option value="outgoing">{{ __('excel.Outgoing by pay date') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</header>
|
||||
@@ -60,7 +62,6 @@
|
||||
let vm = this;
|
||||
axios.post('/excel', this.data, {responseType: "blob"})
|
||||
.then(function(response) {
|
||||
console.log(response);
|
||||
let url = window.URL.createObjectURL(response.data);
|
||||
let a = document.createElement('a');
|
||||
a.href = url;
|
||||
@@ -71,6 +72,12 @@
|
||||
if (vm.data.report === 'payment') {
|
||||
a.download = '{{ __('invoice.Payments') }}' + '.xlsx';
|
||||
}
|
||||
if (vm.data.report === 'incoming') {
|
||||
a.download = '{{ __('incoming.Incoming') }}' + '.xlsx';
|
||||
}
|
||||
if (vm.data.report === 'outgoing') {
|
||||
a.download = '{{ __('incoming.Outgoing') }}' + '.xlsx';
|
||||
}
|
||||
let download = document.querySelector('#download');
|
||||
download.appendChild(a);
|
||||
a.click();
|
||||
|
||||
310
resources/views/incoming/create.blade.php
Normal file
310
resources/views/incoming/create.blade.php
Normal file
@@ -0,0 +1,310 @@
|
||||
<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') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12" x-data="incomingForm()">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<!-- Incoming data -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('incoming.Incoming data') }}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<form class="mt-6 space-y-6" @submit.prevent="">
|
||||
|
||||
<div class="flex flex-row space-x-8 items-start relative">
|
||||
<div class="w-1/2 grid grid-cols-3 items-center">
|
||||
<x-input-label for="name" :value="__('invoice.Invoice Number')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.invoice_number"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Invoice Date')"/>
|
||||
<x-text-input id="name" name="name" type="date"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.issue_date"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Invoice Due date')"/>
|
||||
<x-text-input id="name" name="name" type="date"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.due_date"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Type code')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus disabled
|
||||
autocomplete="name" x-model="incoming.invoice_type_code"/>
|
||||
|
||||
<x-input-label for="name" :value="__('common.Currency code')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus disabled
|
||||
autocomplete="name" x-model="incoming.currency_code"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Net')"/>
|
||||
<x-text-input id="name" name="name" type="number" step="0.01"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus disabled
|
||||
autocomplete="net" x-model="incoming.net"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Gross')"/>
|
||||
<x-text-input id="name" name="name" type="number" step="0.01"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus disabled
|
||||
autocomplete="gross" x-model="incoming.gross"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Tax')"/>
|
||||
<x-text-input id="name" name="name" type="number" step="0.01"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus disabled
|
||||
autocomplete="tax" x-model="incoming.tax"/>
|
||||
</div>
|
||||
<div class="w-1/2 grid grid-cols-3 items-center">
|
||||
<x-input-label for="name" :value="__('invoice.Account holder')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.pay_name"/>
|
||||
|
||||
<x-input-label for="name" :value="__('configuration.IBAN')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.pay_iban"/>
|
||||
|
||||
<x-input-label for="name" :value="__('configuration.BIC')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.pay_bic"/>
|
||||
|
||||
<x-input-label for="name" :value="__('common.Paid at')"/>
|
||||
<x-text-input id="name" name="name" type="date"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.pay_date"/>
|
||||
|
||||
</div>
|
||||
|
||||
<x-primary-button x-on:click="submit();" class="absolute right-0 bottom-0">{{ __('form.Save') }}</x-primary-button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Supplier data -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('supplier.Supplier') }}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<form class="mt-6 space-y-6" @submit.prevent="">
|
||||
|
||||
<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')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="supplier.name"/>
|
||||
|
||||
<x-input-label for="registration_name" :value="__('common.Registration name')"/>
|
||||
<x-text-input id="registration_name" name="registration_name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('registration_name')" autofocus
|
||||
autocomplete="registration_name"
|
||||
x-model="supplier.registration_name"/>
|
||||
|
||||
<x-input-label for="email" :value="__('common.Email')"/>
|
||||
<x-text-input id="email" name="email" type="email"
|
||||
class="mt-1 col-span-2" :value="old('email')"
|
||||
autocomplete="email" x-model="supplier.email"/>
|
||||
|
||||
<x-input-label for="address" :value="__('common.Address')"/>
|
||||
<x-text-input id="address" name="address" type="text"
|
||||
class="mt-1 col-span-2" :value="old('address')"
|
||||
autocomplete="address"
|
||||
x-model="supplier.address"/>
|
||||
|
||||
<x-input-label for="zip" :value="__('common.Zip Code')"/>
|
||||
<x-text-input id="zip" name="zip" type="text"
|
||||
class="mt-1 col-span-2" :value="old('zip')"
|
||||
autocomplete="zip"
|
||||
x-model="supplier.zip"/>
|
||||
|
||||
<x-input-label for="city" :value="__('common.City')"/>
|
||||
<x-text-input id="city" name="city" type="text"
|
||||
class="mt-1 col-span-2" :value="old('city')"
|
||||
autocomplete="city"
|
||||
x-model="supplier.city"/>
|
||||
</div>
|
||||
|
||||
<div class="w-1/2 grid grid-cols-3 items-center">
|
||||
<x-input-label for="country_code" :value="__('common.Country code')"/>
|
||||
<x-text-input id="country_code" name="country_code" type="text"
|
||||
class="mt-1 col-span-2" :value="old('country_code')"
|
||||
autocomplete="country_code"
|
||||
x-model="supplier.country_code"/>
|
||||
|
||||
<x-input-label for="tax_fc" :value="__('common.Tax FC')"/>
|
||||
<x-text-input id="tax_fc" name="tax_fc" type="text"
|
||||
class="mt-1 col-span-2" :value="old('tax_fc')"
|
||||
autocomplete="tax_fc"
|
||||
x-model="supplier.tax_fc"/>
|
||||
|
||||
<x-input-label for="tax_vat" :value="__('common.Tax VAT')"/>
|
||||
<x-text-input id="tax_vat" name="tax_vat" type="text"
|
||||
class="mt-1 col-span-2" :value="old('tax_vat')"
|
||||
autocomplete="tax_vat"
|
||||
x-model="supplier.tax_vat"/>
|
||||
|
||||
<x-input-label for="contact_name" :value="__('common.Contact name')"/>
|
||||
<x-text-input id="contact_name" name="contact_name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('contact_name')"
|
||||
autocomplete="contact_name"
|
||||
x-model="supplier.contact_name"/>
|
||||
|
||||
<x-input-label for="contact_phone" :value="__('common.Contact phone')"/>
|
||||
<x-text-input id="contact_phone" name="contact_phone" type="text"
|
||||
class="mt-1 col-span-2" :value="old('contact_phone')"
|
||||
autocomplete="contact_phone"
|
||||
x-model="supplier.contact_phone"/>
|
||||
|
||||
<x-input-label for="contact_email" :value="__('common.Contact email')"/>
|
||||
<x-text-input id="contact_email" name="t" type="email"
|
||||
class="mt-1 col-span-2" :value="old('contact_email')"
|
||||
autocomplete="contact_email"
|
||||
x-model="supplier.contact_email"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Incoming taxes -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100 relative">
|
||||
{{ __('invoice.Tax items') }}
|
||||
<x-primary-button x-on:click="addTax();" class="absolute right-0">+</x-primary-button>
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<div class="flex flex-row items-end gap-2 w-full">
|
||||
<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"/>
|
||||
<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].taxable_amount" name="taxes[index].taxable_amount" type="number"
|
||||
class="mt-1 block w-1/6"
|
||||
autofocus x-on:blur="calculateTax(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);"
|
||||
x-model="taxes[index].percentage"/>
|
||||
|
||||
<x-text-input id="taxes[index].currency" name="taxes[index].currency" type="text"
|
||||
class="mt-1 block w-1/6"
|
||||
autofocus disabled
|
||||
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">
|
||||
-
|
||||
</x-danger-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<script>
|
||||
function incomingForm() {
|
||||
return {
|
||||
incoming: {
|
||||
invoice_number: 'R01',
|
||||
issue_date: "{{ \Illuminate\Support\Facades\Date::now()->format('Y-m-d') }}",
|
||||
due_date: "{{ \Illuminate\Support\Facades\Date::now()->addDays(14)->format('Y-m-d') }}",
|
||||
invoice_type_code: '380',
|
||||
currency_code: 'EUR',
|
||||
net: 0,
|
||||
gross: 0,
|
||||
tax: 0
|
||||
},
|
||||
supplier: {},
|
||||
taxes: [],
|
||||
|
||||
addTax() {
|
||||
let tax = {
|
||||
taxable_amount: 0,
|
||||
amount: 0,
|
||||
percentage: 0,
|
||||
currency: this.incoming.currency_code
|
||||
};
|
||||
this.taxes.push(tax);
|
||||
},
|
||||
|
||||
calculateTax(index) {
|
||||
this.taxes[index].amount = this.taxes[index].taxable_amount * 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);
|
||||
},
|
||||
|
||||
deleteTax(index) {
|
||||
this.taxes.splice(index, 1);
|
||||
this.calculateSum();
|
||||
},
|
||||
|
||||
submit() {
|
||||
axios.post('/incoming', {incoming: this.incoming, taxes: this.taxes, supplier: this.supplier})
|
||||
.then(function(response) {
|
||||
window.location.href = '/incoming';
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
394
resources/views/incoming/edit.blade.php
Normal file
394
resources/views/incoming/edit.blade.php
Normal file
@@ -0,0 +1,394 @@
|
||||
<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') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12" x-data="incomingForm()">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<!-- Incoming data -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('incoming.Incoming data') }}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<form class="mt-6 space-y-6" @submit.prevent="">
|
||||
|
||||
<div class="flex flex-row space-x-8 items-start relative">
|
||||
<div class="w-1/2 grid grid-cols-3 items-center">
|
||||
<x-input-label for="name" :value="__('invoice.Invoice Number')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.invoice_number"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Invoice Date')"/>
|
||||
<x-text-input id="name" name="name" type="date"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.issue_date"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Invoice Due date')"/>
|
||||
<x-text-input id="name" name="name" type="date"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.due_date"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Type code')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.invoice_type_code"/>
|
||||
|
||||
<x-input-label for="name" :value="__('common.Currency code')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.currency_code"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Net')"/>
|
||||
<x-text-input id="name" name="name" type="number" step="0.01"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.net"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Gross')"/>
|
||||
<x-text-input id="name" name="name" type="number" step="0.01"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.gross"/>
|
||||
|
||||
<x-input-label for="name" :value="__('invoice.Tax')"/>
|
||||
<x-text-input id="name" name="name" type="number" step="0.01"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.tax"/>
|
||||
</div>
|
||||
<div class="w-1/2 grid grid-cols-3 items-center">
|
||||
<x-input-label for="name" :value="__('invoice.Account holder')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.pay_name"/>
|
||||
|
||||
<x-input-label for="name" :value="__('configuration.IBAN')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.pay_iban"/>
|
||||
|
||||
<x-input-label for="name" :value="__('configuration.BIC')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.pay_bic"/>
|
||||
|
||||
<x-input-label for="name" :value="__('common.Paid at')"/>
|
||||
<x-text-input id="name" name="name" type="date"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="incoming.pay_date"/>
|
||||
|
||||
</div>
|
||||
|
||||
<x-primary-button x-on:click="submit();" class="absolute right-0 bottom-0">{{ __('form.Save') }}</x-primary-button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Supplier data -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('supplier.Supplier') }}
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<form class="mt-6 space-y-6" @submit.prevent="">
|
||||
|
||||
<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')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('name')" autofocus
|
||||
autocomplete="name" x-model="supplier.name"/>
|
||||
|
||||
<x-input-label for="registration_name" :value="__('common.Registration name')"/>
|
||||
<x-text-input id="registration_name" name="registration_name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('registration_name')" autofocus
|
||||
autocomplete="registration_name"
|
||||
x-model="supplier.registration_name"/>
|
||||
|
||||
<x-input-label for="email" :value="__('common.Email')"/>
|
||||
<x-text-input id="email" name="email" type="email"
|
||||
class="mt-1 col-span-2" :value="old('email')"
|
||||
autocomplete="email" x-model="supplier.email"/>
|
||||
|
||||
<x-input-label for="address" :value="__('common.Address')"/>
|
||||
<x-text-input id="address" name="address" type="text"
|
||||
class="mt-1 col-span-2" :value="old('address')"
|
||||
autocomplete="address"
|
||||
x-model="supplier.address"/>
|
||||
|
||||
<x-input-label for="zip" :value="__('common.Zip Code')"/>
|
||||
<x-text-input id="zip" name="zip" type="text"
|
||||
class="mt-1 col-span-2" :value="old('zip')"
|
||||
autocomplete="zip"
|
||||
x-model="supplier.zip"/>
|
||||
|
||||
<x-input-label for="city" :value="__('common.City')"/>
|
||||
<x-text-input id="city" name="city" type="text"
|
||||
class="mt-1 col-span-2" :value="old('city')"
|
||||
autocomplete="city"
|
||||
x-model="supplier.city"/>
|
||||
</div>
|
||||
|
||||
<div class="w-1/2 grid grid-cols-3 items-center">
|
||||
<x-input-label for="country_code" :value="__('common.Country code')"/>
|
||||
<x-text-input id="country_code" name="country_code" type="text"
|
||||
class="mt-1 col-span-2" :value="old('country_code')"
|
||||
autocomplete="country_code"
|
||||
x-model="supplier.country_code"/>
|
||||
|
||||
<x-input-label for="tax_fc" :value="__('common.Tax FC')"/>
|
||||
<x-text-input id="tax_fc" name="tax_fc" type="text"
|
||||
class="mt-1 col-span-2" :value="old('tax_fc')"
|
||||
autocomplete="tax_fc"
|
||||
x-model="supplier.tax_fc"/>
|
||||
|
||||
<x-input-label for="tax_vat" :value="__('common.Tax VAT')"/>
|
||||
<x-text-input id="tax_vat" name="tax_vat" type="text"
|
||||
class="mt-1 col-span-2" :value="old('tax_vat')"
|
||||
autocomplete="tax_vat"
|
||||
x-model="supplier.tax_vat"/>
|
||||
|
||||
<x-input-label for="contact_name" :value="__('common.Contact name')"/>
|
||||
<x-text-input id="contact_name" name="contact_name" type="text"
|
||||
class="mt-1 col-span-2" :value="old('contact_name')"
|
||||
autocomplete="contact_name"
|
||||
x-model="supplier.contact_name"/>
|
||||
|
||||
<x-input-label for="contact_phone" :value="__('common.Contact phone')"/>
|
||||
<x-text-input id="contact_phone" name="contact_phone" type="text"
|
||||
class="mt-1 col-span-2" :value="old('contact_phone')"
|
||||
autocomplete="contact_phone"
|
||||
x-model="supplier.contact_phone"/>
|
||||
|
||||
<x-input-label for="contact_email" :value="__('common.Contact email')"/>
|
||||
<x-text-input id="contact_email" name="t" type="email"
|
||||
class="mt-1 col-span-2" :value="old('contact_email')"
|
||||
autocomplete="contact_email"
|
||||
x-model="supplier.contact_email"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Incoming items -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100 relative">
|
||||
{{ __('invoice.Invoice items') }}
|
||||
<x-primary-button x-on:click="addItem();" class="absolute right-0">+</x-primary-button>
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<div class="flex flex-row items-end gap-2 w-full">
|
||||
<x-input-label for="invoice_item.amount" :value="__('invoice.Amount')" class="w-1/12"/>
|
||||
<x-input-label for="invoice_item.name" :value="__('invoice.Name') . ' / ' . __('invoice.Article number') . ' / ' . __('invoice.Description')" class="w-1/2"/>
|
||||
<x-input-label for="invoice_item.price" :value="__('invoice.Price')" class="w-1/12"/>
|
||||
<x-input-label for="invoice_item.discount" :value="__('invoice.Discount')" class="w-1/12"/>
|
||||
<x-input-label for="invoice_item.tax" :value="__('invoice.Tax')" class="w-1/12"/>
|
||||
<x-input-label for="invoice_item.tax" :value="__('invoice.Net')" class="w-1/12"/>
|
||||
<div class="w-1/12 relative h-10"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<template x-for="(item, index) in items">
|
||||
<div>
|
||||
<div class="flex flex-row items-end gap-2 w-full relative">
|
||||
<x-text-input id="items[index].amount" name="items[index].amount" type="number"
|
||||
class="mt-1 block w-1/12"
|
||||
autofocus
|
||||
x-model="items[index].amount"/>
|
||||
|
||||
<x-text-input id="items[index].name" name="items[index].name" type="text"
|
||||
class="mt-1 block w-1/2"
|
||||
autofocus
|
||||
placeholder="{{ __('invoice.Name') }}"
|
||||
x-model="items[index].name"/>
|
||||
|
||||
<x-text-input id="items[index].price" name="items[index].price" type="number"
|
||||
class="mt-1 block w-1/12"
|
||||
autofocus
|
||||
x-model="items[index].price"/>
|
||||
|
||||
<x-text-input id="items[index].discount" name="items[index].discount" type="number"
|
||||
class="mt-1 block w-1/12"
|
||||
autofocus
|
||||
x-model="items[index].discount"/>
|
||||
|
||||
<x-text-input id="items[index].tax" name="items[index].tax" type="number"
|
||||
class="mt-1 block w-1/12"
|
||||
autofocus
|
||||
x-model="items[index].tax"/>
|
||||
|
||||
<x-text-input id="items[index].total" name="items[index].total" type="number"
|
||||
class="mt-1 block w-1/12"
|
||||
autofocus
|
||||
x-model="items[index].total"/>
|
||||
|
||||
<div class="flex flex-row w-1/12 h-10 relative">
|
||||
<x-danger-button x-on:click="deleteItem(index);" class="absolute right-0">
|
||||
-
|
||||
</x-danger-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<div class="w-1/12"></div>
|
||||
<div class="flex flex-col w-1/2 gap-2">
|
||||
<x-text-input id="items[index].article_number" name="items[index].article_number" type="text"
|
||||
class="mt-1 block full+"
|
||||
autofocus
|
||||
placeholder="{{ __('invoice.Article number') }}"
|
||||
x-model="items[index].article_number"/>
|
||||
|
||||
|
||||
<textarea placeholder="{{ __('invoice.Description') }}"
|
||||
name="items[index].description" x-model="items[index].description"
|
||||
x-text="items[index].description"
|
||||
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 mt-1 block w-full"></textarea>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Incoming taxes -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100 relative">
|
||||
{{ __('invoice.Tax items') }}
|
||||
<x-primary-button x-on:click="addTax();" class="absolute right-0">+</x-primary-button>
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<div class="flex flex-row items-end gap-2 w-full">
|
||||
<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"/>
|
||||
<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].taxable_amount" name="taxes[index].taxable_amount" type="number"
|
||||
class="mt-1 block w-1/6"
|
||||
autofocus
|
||||
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
|
||||
x-model="taxes[index].percentage"/>
|
||||
|
||||
<x-text-input id="taxes[index].currency" name="taxes[index].currency" type="text"
|
||||
class="mt-1 block w-1/6"
|
||||
autofocus
|
||||
placeholder="{{ __('invoice.Name') }}"
|
||||
x-model="taxes[index].currency"/>
|
||||
|
||||
<div class="flex flex-row w-1/12 h-10 relative">
|
||||
<x-danger-button x-on:click="deleteTax(index);" class="absolute right-0">
|
||||
-
|
||||
</x-danger-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<script>
|
||||
function incomingForm() {
|
||||
return {
|
||||
incoming_id: {!! $incoming->id !!},
|
||||
incoming: {!! $incoming !!},
|
||||
supplier: {!! $supplier !!},
|
||||
items: {!! $items !!},
|
||||
taxes: {!! $taxes !!},
|
||||
|
||||
addItem() {
|
||||
let item = {
|
||||
incoming_id: this.incoming_id,
|
||||
name: '',
|
||||
article_number: '',
|
||||
description: '',
|
||||
amount: 0,
|
||||
discount: 0,
|
||||
tax: 0,
|
||||
price: 0,
|
||||
total: 0
|
||||
};
|
||||
this.items.push(item);
|
||||
},
|
||||
|
||||
addTax() {
|
||||
let tax = {
|
||||
incoming_id: this.incoming_id,
|
||||
taxable_amount: 0,
|
||||
amount: 0,
|
||||
percentage: 0,
|
||||
currency: 'EUR'
|
||||
};
|
||||
this.taxes.push(tax);
|
||||
},
|
||||
|
||||
deleteItem(index) {
|
||||
this.items.splice(index, 1);
|
||||
},
|
||||
|
||||
deleteTax(index) {
|
||||
this.taxes.splice(index, 1);
|
||||
},
|
||||
|
||||
submit() {
|
||||
axios.put('/incoming/' + this.incoming_id, {incoming: this.incoming, items: this.items, taxes: this.taxes, supplier: this.supplier})
|
||||
.then(function(response) {
|
||||
window.location.href = '/incoming';
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
143
resources/views/incoming/index.blade.php
Normal file
143
resources/views/incoming/index.blade.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<div class="flex flex-row w-full">
|
||||
<h2 class="grow font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('incoming.Incoming') }}
|
||||
</h2>
|
||||
<a href="{{ route('excel') }}" class="relative flex flex-row">
|
||||
<x-excel-icon class="text-gray-800 cursor-pointer"/>
|
||||
</a>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w flex flex-row">
|
||||
<section class="w-1/2">
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('incoming.Add new invoice') }}
|
||||
</h2>
|
||||
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ __("incoming.Add new invoice by clicking add") }}
|
||||
</p>
|
||||
</header>
|
||||
<a class="mt-6 inline-block" href="{{ route('incoming.create') }}"><x-primary-button>{{ __('form.Add') }}</x-primary-button></a>
|
||||
</section>
|
||||
<section class="w-1/2">
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('incoming.Upload new invoice') }}
|
||||
</h2>
|
||||
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ __("incoming.Upload new xml invoice by clicking upload") }}
|
||||
</p>
|
||||
</header>
|
||||
<a class="mt-6 inline-block" href="{{ route('incoming.upload') }}"><x-primary-button>{{ __('form.Upload') }}</x-primary-button></a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w" x-data="incomingForm">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('incoming.Existing invoices') }}
|
||||
</h2>
|
||||
<div class="flex flex-row space-x-4 items-center mt-4">
|
||||
<x-input-label for="from" :value="__('invoice.From')"/>
|
||||
<x-text-input type="date" id="from" name="from" x-model="from" x-on:change="fetchInvoices()"/>
|
||||
<x-input-label for="end" :value="__('invoice.End')"/>
|
||||
<x-text-input type="date" id="end" name="end" x-model="end" x-on:change="fetchInvoices()"/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<summary class="cursor-pointer flex flex-row w-full mt-4">
|
||||
<div class="w-1/6 font-bold border-b-2">{{ __('invoice.Invoice Number') }}</div>
|
||||
<div class="w-1/3 font-bold border-b-2">{{ __('supplier.Supplier') }}</div>
|
||||
<div class="w-1/4 font-bold border-b-2">{{ __('common.Name') }}</div>
|
||||
<div class="w-1/12 font-bold border-b-2 text-right">{{ __('invoice.Sum') }}</div>
|
||||
<div class="w-1/12 font-bold border-b-2 text-right">{{ __('common.Created at') }}</div>
|
||||
<div class="w-1/12 font-bold border-b-2 text-right">{{ __('common.Paid at') }}</div>
|
||||
</summary>
|
||||
|
||||
<template x-for="invoice in invoices">
|
||||
<details class="even:bg-gray-100 odd:bg-white">
|
||||
<summary class="cursor-pointer flex flex-row w-full" @click="window.location.href='/incoming/' + invoice.id + '/edit';">
|
||||
<div class="w-1/6" x-text="invoice.invoice_number"></div>
|
||||
<div class="w-1/3" x-text="invoice.supplier.name"></div>
|
||||
<div class="w-1/4" x-text="invoice.supplier.email"></div>
|
||||
<div class="w-1/12 text-right" x-text="invoice.gross + ' €'"></div>
|
||||
<div class="w-1/12 text-right" x-text="invoice.created"></div>
|
||||
<div class="w-1/12 text-right" x-text="invoice.paid"></div>
|
||||
</summary>
|
||||
</details>
|
||||
</template>
|
||||
|
||||
<div class="w-full border-t-2"></div>
|
||||
|
||||
<div class="w-1/2 grid grid-cols-2">
|
||||
<div>{{ __('invoice.Net') }}</div>
|
||||
<div class="text-right" x-text="net + ' €'"></div>
|
||||
<div>{{ __('invoice.Tax') }}</div>
|
||||
<div class="text-right" x-text="tax + ' €'"></div>
|
||||
<div class="font-bold">{{ __('invoice.Sum') }}</div>
|
||||
<div class="font-bold text-right" x-text="gross + ' €'"></div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<script>
|
||||
function incomingForm() {
|
||||
return {
|
||||
net: 0,
|
||||
gross: 0,
|
||||
tax: 0,
|
||||
from: "{{ \Illuminate\Support\Facades\Date::now()->firstOfMonth()->format('Y-m-d') }}",
|
||||
end: "{{ \Illuminate\Support\Facades\Date::now()->format('Y-m-d') }}",
|
||||
invoices: [],
|
||||
|
||||
init() {
|
||||
this.fetchInvoices();
|
||||
},
|
||||
|
||||
fetchInvoices() {
|
||||
let vm = this;
|
||||
axios.get('/incoming-filter/' + this.from + '/' + this.end)
|
||||
.then(function (response) {
|
||||
vm.invoices = response.data;
|
||||
vm.calculateSum();
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
})
|
||||
},
|
||||
|
||||
calculateSum() {
|
||||
this.net = 0;
|
||||
this.gross = 0;
|
||||
this.tax = 0;
|
||||
for (const [key, invoice] of Object.entries(this.invoices)) {
|
||||
this.net += parseFloat(invoice.net);
|
||||
this.gross += parseFloat(invoice.gross);
|
||||
this.tax += parseFloat(invoice.tax)
|
||||
}
|
||||
this.net = this.net.toFixed(2)
|
||||
this.gross = this.gross.toFixed(2);
|
||||
this.tax = this.tax.toFixed(2);
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
77
resources/views/incoming/upload.blade.php
Normal file
77
resources/views/incoming/upload.blade.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<div class="flex flex-row w-full">
|
||||
<h2 class="grow font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('incoming.Upload new invoice') }}
|
||||
</h2>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12" x-data="uploadForm">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w ">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('incoming.Upload new invoice') }}
|
||||
</h2>
|
||||
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ __("incoming.Upload new xml invoice by clicking upload") }}
|
||||
</p>
|
||||
</header>
|
||||
</section>
|
||||
<form class="mt-6 space-y-2" @submit.prevent="">
|
||||
<p class="text-red-600 font-bold" x-text="message" x-show="error"></p>
|
||||
<div class="flex flex-row items-start">
|
||||
<x-input-label class="w-1/4" for="upload_file"
|
||||
:value="__('incoming.Invoice file')"/>
|
||||
<x-text-input id="upload_file" name="upload_file" type="file" accept=".xml"
|
||||
class="mt-1 block w-full"
|
||||
@change="checkUpload()"/>
|
||||
</div>
|
||||
<x-primary-button x-show="may_upload"
|
||||
@click="submit()">{{ __('form.Upload') }}</x-primary-button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<script>
|
||||
function uploadForm() {
|
||||
return {
|
||||
error: false,
|
||||
message: '',
|
||||
may_upload: false,
|
||||
upload_file: null,
|
||||
|
||||
checkUpload() {
|
||||
let reader, files = document.getElementById('upload_file').files;
|
||||
reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
this.may_upload = true;
|
||||
this.upload_file = e.target.result;
|
||||
};
|
||||
reader.readAsDataURL(files[0]);
|
||||
},
|
||||
|
||||
submit() {
|
||||
let vm = this;
|
||||
axios.post('incoming-upload', {uploadFile: this.upload_file})
|
||||
.then(function (response) {
|
||||
window.location.href = '/incoming/' + response.data.id + '/edit';
|
||||
})
|
||||
.catch(function (error) {
|
||||
vm.error = true;
|
||||
vm.may_upload = false;
|
||||
vm.message = error.response.data.message;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -31,6 +31,10 @@
|
||||
:active="\Illuminate\Support\Str::startsWith(request()->route()->getName(), 'taxrate.')">
|
||||
{{ __('configuration.Taxrates') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('incoming.index')"
|
||||
:active="\Illuminate\Support\Str::startsWith(request()->route()->getName(), 'incoming.')">
|
||||
{{ __('incoming.Incoming') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('option.index')"
|
||||
:active="\Illuminate\Support\Str::startsWith(request()->route()->getName(), 'option.')">
|
||||
{{ __('configuration.Options') }}
|
||||
|
||||
@@ -4,6 +4,7 @@ use App\Http\Controllers\Api\AddressController;
|
||||
use App\Http\Controllers\Api\AuthController;
|
||||
use App\Http\Controllers\Api\CustomerController;
|
||||
use App\Http\Controllers\Api\ExcelController;
|
||||
use App\Http\Controllers\Api\IncomingController;
|
||||
use App\Http\Controllers\Api\InvoiceController;
|
||||
use App\Http\Controllers\Api\InvoiceitemController;
|
||||
use App\Http\Controllers\Api\MailController;
|
||||
@@ -40,6 +41,10 @@ Route::group(['as' => 'api.'], function () {
|
||||
Route::get('/payment-filter/{start}/{end}', [PaymentController::class, 'indexFilter'])->name('payment.index');
|
||||
Route::apiResource('/invoice.payment', PaymentController::class)->shallow();
|
||||
Route::post('/excel', [ExcelController::class, 'export'])->name('excel.export');
|
||||
Route::post('/incoming-upload', [IncomingController::class, 'upload'])->name('incoming.upload');
|
||||
Route::get('/incoming-filter/{start}/{end}', [IncomingController::class, 'index'])->name('incoming.index');
|
||||
Route::put('/incoming/{incoming}', [IncomingController::class, 'update'])->name('incoming.update');
|
||||
Route::post('/incoming', [IncomingController::class, 'store'])->name('incoming.store');
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use App\Http\Controllers\AddressController;
|
||||
use App\Http\Controllers\CustomerController;
|
||||
use App\Http\Controllers\EController;
|
||||
use App\Http\Controllers\IncomingController;
|
||||
use App\Http\Controllers\InvoiceController;
|
||||
use App\Http\Controllers\OptionController;
|
||||
use App\Http\Controllers\PaymentController;
|
||||
@@ -34,6 +35,8 @@ Route::middleware('auth')->group(function () {
|
||||
Route::get('/invoice/{id}/mail', [InvoiceController::class, 'mail'])->name('invoice.mail');
|
||||
Route::resource('/payment', PaymentController::class)->only(['index', 'create', 'edit']);
|
||||
Route::get('/excel', function() { return view('excel'); })->name('excel');
|
||||
Route::resource('/incoming', IncomingController::class)->only(['index', 'create', 'edit']);
|
||||
Route::get('/incoming-upload', [IncomingController::class, 'upload'])->name('incoming.upload');
|
||||
});
|
||||
|
||||
require __DIR__.'/auth.php';
|
||||
|
||||
Reference in New Issue
Block a user