Make invoices editable and build possibility to send them manually.
This commit is contained in:
@@ -55,9 +55,31 @@ class InvoiceController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*/
|
*/
|
||||||
public function update(Request $request, Invoice $invoice)
|
public function update(Request $request, Invoice $invoice): JsonResponse
|
||||||
{
|
{
|
||||||
//
|
$invoiceData = $request->validate([
|
||||||
|
'customer_id' => 'required|integer|exists:customers,id',
|
||||||
|
'address_id' => 'required|integer|exists:addresses,id',
|
||||||
|
'delivery_id' => 'nullable|integer|exists:addresses,id',
|
||||||
|
'tax' => 'required|numeric',
|
||||||
|
'sum' => 'required|numeric',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$invoice->update($invoiceData);
|
||||||
|
$invoice->items()->delete();
|
||||||
|
|
||||||
|
return response()->json($invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function state(Request $request, Invoice $invoice): JsonResponse
|
||||||
|
{
|
||||||
|
$invoiceData = $request->validate([
|
||||||
|
'status' => 'required|string'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$invoice->update($invoiceData);
|
||||||
|
|
||||||
|
return response()->json($invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ class InvoiceController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Show the form for editing the specified resource.
|
* Show the form for editing the specified resource.
|
||||||
*/
|
*/
|
||||||
public function edit(Invoice $invoice)
|
public function edit(Invoice $invoice): View
|
||||||
{
|
{
|
||||||
//
|
return view('invoice.edit', ['invoice' => $invoice]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
489
resources/views/invoice/edit.blade.php
Normal file
489
resources/views/invoice/edit.blade.php
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
<x-app-layout>
|
||||||
|
<x-slot name="header">
|
||||||
|
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||||
|
{{ __('invoice.Edit invoice') }}
|
||||||
|
</h2>
|
||||||
|
</x-slot>
|
||||||
|
|
||||||
|
@if($invoice->status === 'created')
|
||||||
|
<div class="py-12" x-data="invoiceForm()">
|
||||||
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||||
|
|
||||||
|
<!-- Customer 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">
|
||||||
|
{{ __('invoice.Select customer') }}
|
||||||
|
</h2>
|
||||||
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
{{ __("invoice.Select your customer and address") }}
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="my-8">
|
||||||
|
<x-input-label for="search" :value="__('common.Search')"/>
|
||||||
|
<x-text-input id="search" name="search" type="search" class="mt-1 block w-full"
|
||||||
|
x-ref="searchInput"
|
||||||
|
autofocus
|
||||||
|
placeholder="{{ __('invoice.Search customer') }}"
|
||||||
|
x-on:keydown.window.prevent.slash="$refs.searchInput.focus()"
|
||||||
|
x-model="search"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<template x-for="(customer, index) in getFilteredCustomer()">
|
||||||
|
<div class="cursor-pointer grid grid-cols-4 even:bg-gray-100 odd:bg-white"
|
||||||
|
:class="customer.id == customer_id ? 'font-bold' : ''"
|
||||||
|
x-on:click="getAddress(index);">
|
||||||
|
<div x-text="customer.name"></div>
|
||||||
|
<div x-text="customer.email"></div>
|
||||||
|
<div x-text="(customer.address) ? customer.address.address : ''"></div>
|
||||||
|
<div x-text="(customer.address) ? customer.address.city : ''"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Address 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">
|
||||||
|
{{ __('invoice.Select address') }}
|
||||||
|
</h2>
|
||||||
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
{{ __("invoice.Select your customer's address") }}
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="flex flex-row my-8">
|
||||||
|
<div class="w-1/2">
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('customer.Invoice Address') }}</div>
|
||||||
|
<x-danger-button class="ml-4 px-0 py-0" x-show="address_id != 0">
|
||||||
|
<x-trash-icon class="h-5 p-0" x-on:click="address_id = 0; address = {}"/>
|
||||||
|
</x-danger-button>
|
||||||
|
</div>
|
||||||
|
<select class="absolute z-10 bg-white 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" id="select_address_id" x-show="address_id == 0" x-on:click="setInvoiceAddress()">
|
||||||
|
<template x-for="addr in addresses[customer_id]" >
|
||||||
|
<option :value="addr.id" x-text="addr.name + '; ' + addr.address + '; ' + addr.zip + ' ' + addr.city + '; ' + addr.phone + '; ' + addr.email"></option>
|
||||||
|
</template>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div x-text="address.name"></div>
|
||||||
|
<div x-text="address.address"></div>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div class="mr-2" x-text="address.zip"></div>
|
||||||
|
<div x-text="address.city"></div>
|
||||||
|
</div>
|
||||||
|
<div x-text="address.phone"></div>
|
||||||
|
<div x-text="address.email"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="w-1/2">
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('customer.Delivery Address') }}</div>
|
||||||
|
<x-danger-button class="ml-4 px-0 py-0" x-show="delivery_id != null">
|
||||||
|
<x-trash-icon class="h-5 p-0" x-on:click="delivery_id = null; delivery = {}"/>
|
||||||
|
</x-danger-button>
|
||||||
|
</div>
|
||||||
|
<select class="absolute z-10 bg-white 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" id="select_delivery_id" x-show="delivery_id == null" x-on:click="setIDeliveryAddress()">
|
||||||
|
<template x-for="addr in addresses[customer_id]" >
|
||||||
|
<option :value="addr.id" x-text="addr.name + '; ' + addr.address + '; ' + addr.zip + ' ' + addr.city + '; ' + addr.phone + '; ' + addr.email"></option>
|
||||||
|
</template>
|
||||||
|
</select>
|
||||||
|
<div x-text="delivery.name"></div>
|
||||||
|
<div x-text="delivery.address"></div>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div class="mr-2" x-text="delivery.zip"></div>
|
||||||
|
<div x-text="delivery.city"></div>
|
||||||
|
</div>
|
||||||
|
<div x-text="delivery.phone"></div>
|
||||||
|
<div x-text="delivery.email"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Invoice 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">
|
||||||
|
{{ __('invoice.Invoice items') }}
|
||||||
|
</h2>
|
||||||
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
{{ __("invoice.Enter your invoice items. Click add for an additional invoice item.") }}
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- New invoice item -->
|
||||||
|
<div class="flex flex-row items-end gap-2 w-full mt-4">
|
||||||
|
<x-input-label for="invoice_item.amount" :value="__('invoice.Amount')" class="w-1/12"/>
|
||||||
|
<x-input-label for="invoice_item.name" :value="__('invoice.Name')" class="w-2/3"/>
|
||||||
|
<x-input-label for="invoice_item.price" :value="__('invoice.Price')" class="w-1/12"/>
|
||||||
|
<x-input-label for="invoice_item.tax" :value="__('invoice.Tax')" class="w-1/12"/>
|
||||||
|
<div class="w-1/12 relative h-10"></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row items-end gap-2 w-full">
|
||||||
|
<x-text-input id="invoice_item.amount" name="invoice_item.amount" type="number"
|
||||||
|
class="mt-1 block w-1/12"
|
||||||
|
autofocus
|
||||||
|
x-model="invoice_item.amount"/>
|
||||||
|
|
||||||
|
<x-text-input id="invoice_item.name" name="invoice_item.name" type="text"
|
||||||
|
class="mt-1 block w-2/3"
|
||||||
|
autofocus
|
||||||
|
placeholder="{{ __('invoice.Name') }}"
|
||||||
|
x-model="invoice_item.name"/>
|
||||||
|
|
||||||
|
<x-text-input id="invoice_item.price" name="invoice_item.price" type="number"
|
||||||
|
class="mt-1 block w-1/12"
|
||||||
|
autofocus
|
||||||
|
x-model="invoice_item.price"/>
|
||||||
|
|
||||||
|
<select name="invoice_item.tax" x-model="invoice_item.tax" id="invoice_item.tax"
|
||||||
|
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/12">
|
||||||
|
<template x-for="tax in tax_rates">
|
||||||
|
<option x-bind:value="tax.rate" x-text="tax.rate_percentage"
|
||||||
|
:selected="(invoice_item.tax == tax.rate) ? true : tax.active"></option>
|
||||||
|
</template>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="w-1/12 relative h-10">
|
||||||
|
<x-primary-button x-on:click="addItem();" class="absolute right-0">+</x-primary-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row items-end gap-2 w-full">
|
||||||
|
<div class="w-1/12"></div>
|
||||||
|
<textarea placeholder="{{ __('invoice.Description') }}" name="invoice_item.description"
|
||||||
|
x-model="invoice_item.description" x-text="invoice_item.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-2/3 offset-1"></textarea>
|
||||||
|
<div class="w-1/12 h-10"></div>
|
||||||
|
<div class="w-1/12 h-10"></div>
|
||||||
|
<div class="w-1/12 h-10"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Existing invoice items -->
|
||||||
|
<div x-sort="handle" x-bind="items" class="mt-4">
|
||||||
|
<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-2/3"
|
||||||
|
autofocus
|
||||||
|
placeholder="{{ __('invoice.Name') }}"
|
||||||
|
x-model="items[index].name"/>
|
||||||
|
|
||||||
|
<x-text-input id="items[index].price" name="items[index].price" type="text"
|
||||||
|
class="mt-1 block w-1/12"
|
||||||
|
autofocus
|
||||||
|
x-model="items[index].price"/>
|
||||||
|
|
||||||
|
<select name="items[index].tax" x-model="items[index].tax"
|
||||||
|
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/12">
|
||||||
|
<template x-for="tax in tax_rates">
|
||||||
|
<option x-bind:value="tax.rate" x-text="tax.rate_percentage"
|
||||||
|
:selected="items[index].tax == tax.rate"></option>
|
||||||
|
</template>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="flex flex-row w-1/12 h-10 relative">
|
||||||
|
<x-sort-icon x-sort:handle class="cursor-move"/>
|
||||||
|
<x-danger-button x-on:click="deleteItem(index);" class="absolute right-0">
|
||||||
|
-
|
||||||
|
</x-danger-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row items-end gap-2 w-full">
|
||||||
|
<div class="w-1/12"></div>
|
||||||
|
<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-2/3"></textarea>
|
||||||
|
<div class="w-1/12 h-10"></div>
|
||||||
|
<div class="w-1/12 h-10"></div>
|
||||||
|
<div class="w-1/12 h-10"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<x-primary-button x-on:click="submitForm();" class="">{{ __('form.Save') }}</x-primary-button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<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">{!! __('invoice.Already sent') !!}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</x-app-layout>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let self;
|
||||||
|
|
||||||
|
function invoiceForm() {
|
||||||
|
return {
|
||||||
|
card: {},
|
||||||
|
customers: {},
|
||||||
|
customer: {!! $invoice->customer !!},
|
||||||
|
customer_id: {{ $invoice->customer->id }},
|
||||||
|
addresses: {},
|
||||||
|
address_id: {{ $invoice->address_id }},
|
||||||
|
address: {!! $invoice->address !!},
|
||||||
|
delivery_id: {!! (!is_null($invoice->delivery)) ? $invoice->delivery_id : 'null' !!},
|
||||||
|
delivery: {!! (!is_null($invoice->delivery)) ? $invoice->delivery : '{}' !!},
|
||||||
|
tax_rates: {},
|
||||||
|
tax_standard: 0,
|
||||||
|
|
||||||
|
invoice_id: {{ $invoice->id }},
|
||||||
|
|
||||||
|
invoice_item: {},
|
||||||
|
|
||||||
|
items: [],
|
||||||
|
sort: [],
|
||||||
|
|
||||||
|
selection: '',
|
||||||
|
|
||||||
|
search: '',
|
||||||
|
|
||||||
|
error: false,
|
||||||
|
message: '',
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.getCustomerAddresses();
|
||||||
|
this.getCustomers();
|
||||||
|
this.getTaxRates();
|
||||||
|
self = this;
|
||||||
|
},
|
||||||
|
|
||||||
|
newItem() {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
amount: 1,
|
||||||
|
discount: 0,
|
||||||
|
tax: this.tax_standard,
|
||||||
|
price: 0,
|
||||||
|
total: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getCustomers() {
|
||||||
|
let vm = this;
|
||||||
|
|
||||||
|
axios.get('/customer')
|
||||||
|
.then(function (response) {
|
||||||
|
vm.customers = response.data;
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
vm.error = true;
|
||||||
|
vm.message = error.response.data.message;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getTaxRates() {
|
||||||
|
let vm = this;
|
||||||
|
|
||||||
|
axios.get('/taxrate')
|
||||||
|
.then(function (response) {
|
||||||
|
vm.tax_rates = response.data;
|
||||||
|
let test = vm.tax_rates.find(function (tax) {
|
||||||
|
return tax.active;
|
||||||
|
});
|
||||||
|
vm.tax_standard = test.rate;
|
||||||
|
vm.invoice_item = vm.newItem();
|
||||||
|
|
||||||
|
let init_items = {!! $invoice->items !!};
|
||||||
|
init_items.forEach((obj) => {
|
||||||
|
vm.invoice_item = obj;
|
||||||
|
vm.addItem();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getFilteredCustomer() {
|
||||||
|
if (this.search === '') {
|
||||||
|
return this.customers;
|
||||||
|
}
|
||||||
|
return this.customers.filter((customer) => {
|
||||||
|
return customer.name
|
||||||
|
.replace(/ /g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(this.search.replace(/ /g, '').toLowerCase())
|
||||||
|
||
|
||||||
|
customer.email
|
||||||
|
.replace(/ /g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(this.search.replace(/ /g, '').toLowerCase())
|
||||||
|
||
|
||||||
|
customer.street
|
||||||
|
.replace(/ /g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(this.search.replace(/ /g, '').toLowerCase())
|
||||||
|
||
|
||||||
|
customer.city
|
||||||
|
.replace(/ /g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(this.search.replace(/ /g, '').toLowerCase())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getAddress(index) {
|
||||||
|
this.customer = this.customers[index];
|
||||||
|
this.customer_id = this.customer.id;
|
||||||
|
this.address_id = 0;
|
||||||
|
this.delivery_id = null;
|
||||||
|
this.address = {};
|
||||||
|
this.delivery = {};
|
||||||
|
if (this.customer.address) {
|
||||||
|
this.address_id = this.customer.address.id;
|
||||||
|
this.address = this.customer.address;
|
||||||
|
}
|
||||||
|
if (this.customer.delivery) {
|
||||||
|
this.delivery_id = this.customer.delivery.id;
|
||||||
|
this.delivery = this.customer.delivery;
|
||||||
|
}
|
||||||
|
this.getCustomerAddresses();
|
||||||
|
},
|
||||||
|
|
||||||
|
getCustomerAddresses() {
|
||||||
|
if (typeof (this.addresses[this.customer_id]) == 'undefined') {
|
||||||
|
let vm = this;
|
||||||
|
axios.get('/customer/' + this.customer_id + '/address')
|
||||||
|
.then(function (response) {
|
||||||
|
vm.addresses[vm.customer_id] = response.data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addItem() {
|
||||||
|
this.items.push(this.invoice_item);
|
||||||
|
this.sort.push(this.items.length - 1);
|
||||||
|
this.invoice_item = this.newItem();
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItem(index) {
|
||||||
|
this.items.splice(index, 1);
|
||||||
|
let position = this.sort[index];
|
||||||
|
this.sort.splice(index, 1);
|
||||||
|
for (let i = 0; i < this.sort.length; i++) {
|
||||||
|
if (this.sort[i] > position) {
|
||||||
|
this.sort[i] = --this.sort[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
submitForm() {
|
||||||
|
let sum = 0;
|
||||||
|
let tax = 0;
|
||||||
|
let sort_flipped = Object.entries(this.sort)
|
||||||
|
.reduce((obj, [key, value]) => ({...obj, [value]: key}), {});
|
||||||
|
for (let i = 0; i < this.items.length; i++) {
|
||||||
|
this.items[i].total = this.items[i].amount * this.items[i].price * (1 + this.items[i].tax / 100);
|
||||||
|
tax += this.items[i].amount * this.items[i].price * this.items[i].tax / 100;
|
||||||
|
sum += this.items[i].total;
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.put('invoice/' + this.invoice_id, {
|
||||||
|
customer_id: this.customer_id,
|
||||||
|
address_id: this.address_id,
|
||||||
|
delivery_id: this.delivery_id,
|
||||||
|
sum: sum,
|
||||||
|
tax: tax
|
||||||
|
})
|
||||||
|
.then(function (response) {
|
||||||
|
let error = false;
|
||||||
|
let invoice_id = response.data.id;
|
||||||
|
for (let i = 0; i < self.items.length; i++) {
|
||||||
|
let pos = sort_flipped[i];
|
||||||
|
let item = self.items[pos];
|
||||||
|
axios.post('invoice/' + invoice_id + '/item', item)
|
||||||
|
.then(function (response) {
|
||||||
|
console.log(response);
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
error = true;
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!error) {
|
||||||
|
window.location.href = '/invoice/' + invoice_id;
|
||||||
|
}
|
||||||
|
console.log(response);
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
setInvoiceAddress() {
|
||||||
|
let id = document.querySelector('#select_address_id').value;
|
||||||
|
this.address_id = id;
|
||||||
|
let address_key = Object.keys(this.addresses[this.customer_id]).find(key => (this.addresses[this.customer_id][key].id == id ));
|
||||||
|
this.address = this.addresses[this.customer_id][address_key];
|
||||||
|
},
|
||||||
|
|
||||||
|
setIDeliveryAddress() {
|
||||||
|
let id = document.querySelector('#select_delivery_id').value;
|
||||||
|
this.delivery_id = id;
|
||||||
|
let address_key = Object.keys(this.addresses[this.customer_id]).find(key => (this.addresses[this.customer_id][key].id == id ));
|
||||||
|
this.delivery = this.addresses[this.customer_id][address_key];
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
handle(item, position) {
|
||||||
|
if (position > self.sort[item]) {
|
||||||
|
for (let i = 0; i < self.sort.length; i++) {
|
||||||
|
if (self.sort[i] <= position && self.sort[i] > self.sort[item]) {
|
||||||
|
self.sort[i] = --self.sort[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sort[item] = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position < self.sort[item]) {
|
||||||
|
for (let i = 0; i < self.sort.length; i++) {
|
||||||
|
if (self.sort[i] >= position && self.sort[i] < self.sort[item]) {
|
||||||
|
self.sort[i] = ++self.sort[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sort[item] = position;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -7,11 +7,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
|
|
||||||
<div class="py-12">
|
@if($invoice->status === 'created')
|
||||||
|
<div class="py-12" x-data="mailForm">
|
||||||
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6 mb-12">
|
||||||
|
<!-- Customer 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">
|
||||||
|
{{ __('invoice.Manual Mail') }}
|
||||||
|
</h2>
|
||||||
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
{{ __("invoice.Send email to your customer with attachments manually.") }}
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
<div class="flex items-center gap-4 pt-8" x-show="!sent">
|
||||||
|
<x-primary-button @click="updateState"><x-mail-icon class="size-5"/>{{ __('invoice.Sent menually') }}</x-primary-button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||||
<!-- Customer data -->
|
<!-- Customer data -->
|
||||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||||
<div class="max-w" x-data="mailForm">
|
<div class="max-w" >
|
||||||
<section>
|
<section>
|
||||||
<header>
|
<header>
|
||||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||||
@@ -83,7 +105,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-4" x-show="!sent">
|
<div class="flex items-center gap-4" x-show="!sent">
|
||||||
<x-primary-button @click="submit">{{ __('form.Send') }}</x-primary-button>
|
<x-primary-button @click="submit"><x-mail-icon class="size-5"/>{{ __('form.Send') }}</x-primary-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
@@ -92,6 +114,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@else
|
||||||
|
<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">{!! __('invoice.Already sent') !!}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|
||||||
@@ -114,11 +145,21 @@
|
|||||||
this.sent = true;
|
this.sent = true;
|
||||||
axios.post('/sendInvoice', this.data)
|
axios.post('/sendInvoice', this.data)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
console.log(response);
|
window.location.href = '/invoice';
|
||||||
}).catch(function(error) {
|
}).catch(function(error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
})
|
})
|
||||||
console.log(this.data);
|
},
|
||||||
|
|
||||||
|
updateState() {
|
||||||
|
this.sent = true;
|
||||||
|
axios.put('/invoice/' + this.data.id + '/state', {status: 'sent'})
|
||||||
|
.then(function (response) {
|
||||||
|
window.location.href = '/invoice';
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,6 @@
|
|||||||
{{ __('invoice.Invoice') }} {{ $invoice->number }} ({{ $invoice->localized_state }})
|
{{ __('invoice.Invoice') }} {{ $invoice->number }} ({{ $invoice->localized_state }})
|
||||||
</h2>
|
</h2>
|
||||||
<p class="relative flex flex-row">
|
<p class="relative flex flex-row">
|
||||||
@if ($invoice->status === 'created')
|
|
||||||
<x-mail-icon class="cursor-pointer mr-4" onclick="window.location.href = '{{ route('invoice.mail', $invoice->id) }}'"/>
|
|
||||||
@endif
|
|
||||||
<x-pdf-icon class="text-gray-800 cursor-pointer" onclick="window.open('/invoice/{{ $invoice->id }}/pdf-download', '_blank', 'popup=true')"/>
|
<x-pdf-icon class="text-gray-800 cursor-pointer" onclick="window.open('/invoice/{{ $invoice->id }}/pdf-download', '_blank', 'popup=true')"/>
|
||||||
<x-e-icon class="cursor-pointer" onclick="window.open('/invoice/{{ $invoice->id }}/xml-download', '_blank', 'popup=true')"/>
|
<x-e-icon class="cursor-pointer" onclick="window.open('/invoice/{{ $invoice->id }}/xml-download', '_blank', 'popup=true')"/>
|
||||||
</p>
|
</p>
|
||||||
@@ -93,6 +90,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if ($invoice->status === 'created')
|
||||||
|
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||||
|
<div class="max-w">
|
||||||
|
<div class="flex flex-row items-end gap-2 w-full">
|
||||||
|
<x-primary-button onclick="window.location.href = '{{ route('invoice.mail', $invoice->id) }}'"><x-mail-icon class="mr-4"/>{{ __('form.Send') }}</x-primary-button>
|
||||||
|
<x-primary-button onclick="window.location.href = '{{ route('invoice.edit', $invoice->id) }}'"><x-edit-icon class="mr-4"/>{{ __('form.Edit') }}</x-primary-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ Route::group(['as' => 'api.'], function () {
|
|||||||
Route::apiResource('/taxrate', TaxRateController::class)->except(['show']);
|
Route::apiResource('/taxrate', TaxRateController::class)->except(['show']);
|
||||||
Route::get('/invoice/{start?}/{end?}', [InvoiceController::class, 'index'])->name('invoice.index');
|
Route::get('/invoice/{start?}/{end?}', [InvoiceController::class, 'index'])->name('invoice.index');
|
||||||
Route::post('/invoice', [InvoiceController::class, 'store'])->name('invoice.store');
|
Route::post('/invoice', [InvoiceController::class, 'store'])->name('invoice.store');
|
||||||
|
Route::put('/invoice/{invoice}', [InvoiceController::class, 'update'])->name('invoice.update');
|
||||||
|
Route::put('/invoice/{invoice}/state', [InvoiceController::class, 'state'])->name('invoice.state');
|
||||||
Route::apiResource('/invoice.item', InvoiceItemController::class)->shallow();
|
Route::apiResource('/invoice.item', InvoiceItemController::class)->shallow();
|
||||||
Route::get('/option', [OptionController::class, 'index'])->name('option.index');
|
Route::get('/option', [OptionController::class, 'index'])->name('option.index');
|
||||||
Route::post('/option', [OptionController::class, 'store'])->name('option.store');
|
Route::post('/option', [OptionController::class, 'store'])->name('option.store');
|
||||||
|
|||||||
Reference in New Issue
Block a user