Extend invoices and invoice items to keep more information.

This commit is contained in:
2025-01-31 13:04:59 +01:00
parent e2240c017d
commit d8afe4960e
20 changed files with 1475 additions and 640 deletions

View File

@@ -8,34 +8,67 @@
<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 -->
<!-- Create invoice for customer or project -->
<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 class="grid grid-cols-2 gap-12">
<div>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('invoice.Select invoice basis') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("invoice.Should this invoice be created for customer or project?") }}
</p>
</div>
<label for="toggleInfo"
class="inline-flex min-w-[14rem] cursor-pointer items-center justify-between gap-3 rounded-md border border-neutral-300 bg-neutral-50 px-4 py-1.5 dark:border-neutral-700 dark:bg-neutral-900">
<input id="toggleInfo" type="checkbox" class="peer sr-only" role="switch"
:checked="from_project" x-model="from_project"
x-on:change="invoice.customer_id = 0; selected_customer = {}; invoice.project_id = 0; selected_project = {};"/>
<span
class="trancking-wide text-sm font-medium text-neutral-600 peer-checked:text-neutral-900 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-neutral-300 dark:peer-checked:text-white">{{ __('customer.Customer') }}</span>
<div
class="relative h-6 w-11 after:h-5 after:w-5 peer-checked:after:translate-x-5 rounded-full border border-neutral-300 bg-neutral-50 after:absolute after:bottom-0 after:left-[0.0625rem] after:top-0 after:my-auto after:rounded-full after:bg-neutral-600 after:transition-all after:content-[''] peer-checked:bg-sky-500 peer-checked:after:bg-white peer-focus:outline peer-focus:outline-2 peer-focus:outline-offset-2 peer-focus:outline-neutral-800 peer-focus:peer-checked:outline-sky-500 peer-active:outline-offset-0 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:border-neutral-700 dark:bg-neutral-900 dark:after:bg-neutral-300 dark:peer-checked:bg-sky-500 dark:peer-checked:after:bg-white dark:peer-focus:outline-neutral-300 dark:peer-focus:peer-checked:outline-sky-500"
aria-hidden="true"></div>
<span
class="trancking-wide text-sm font-medium text-neutral-600 peer-checked:text-neutral-900 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-neutral-300 dark:peer-checked:text-white">{{ __('project.Project') }}</span>
</label>
</header>
</section>
</div>
</div>
<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>
<!-- Customer data -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-show="!from_project">
<div class="max-w">
<section>
<header class="grid grid-cols-2 gap-12 pb-8">
<div>
<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>
</div>
<div class="flex flex-row items-center gap-8">
<x-input-label for="search_customer" :value="__('common.Search')"/>
<x-text-input id="search_customer" name="search_customer" type="text"
class="mt-1 block w-full"
x-ref="search_customer"
autofocus
placeholder="{{ __('invoice.Search customer') }}"
x-on:keydown.window.prevent.slash="$refs.search_customer.focus()"
x-model="customer_search"/>
</div>
</header>
<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);">
:class="customer.id == invoice.customer_id ? 'font-bold' : ''"
@click="invoice.customer_id = customer.id; selected_customer = customer; getCustomerAddresses(); if(customer.address != null) { invoice.address_id = customer.address.id; selected_address = customer.address; } else { invoice.address_id = 0; selected_address = {}; } if(customer.delivery != null) { invoice.delivery_id = customer.delivery.id; selected_delivery = customer.delivery; } else { invoice.delivery_id = null; selected_delivery = {}; }">
<div x-text="customer.name"></div>
<div x-text="customer.email"></div>
<div x-text="(customer.address) ? customer.address.address : ''"></div>
@@ -45,12 +78,54 @@
</div>
</section>
</div>
</div>
<!-- Project data -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-show="from_project">
<div class="max-w">
<section>
<header class="grid grid-cols-2 gap-12 pb-8">
<div>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('invoice.Select project') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("invoice.Select your project") }}
</p>
</div>
<div class="flex flex-row gap-8 items-center">
<x-input-label for="search_project" :value="__('common.Search')"/>
<x-text-input id="search_project" name="search_project" type="text"
class="mt-1 block w-full"
x-ref="search_project"
autofocus
placeholder="{{ __('invoice.Search project') }}"
x-on:keydown.window.prevent.slash="$refs.search_project.focus()"
x-model="project_search"/>
</div>
</header>
<div>
<template x-for="(project, index) in getFilteredProject()">
<div class="cursor-pointer grid grid-cols-4 even:bg-gray-100 odd:bg-white"
:class="project.id == invoice.project_id ? 'font-bold' : ''"
@click="invoice.project_id = project.id; selected_project = project; invoice.customer_id = project.customer.id; selected_customer = project.customer; getCustomerAddresses();">
<div x-text="project.name"></div>
<div x-text="project.project_number"></div>
<div x-text="project.customer_name"></div>
<div x-text="project.customer_email"></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="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg"
x-show="Object.keys(selected_customer).length > 0">
<div class="max-w">
<section>
<header>
@@ -63,141 +138,225 @@
</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>
<!-- Invoice address -->
<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="invoice.address_id != 0">
<x-trash-icon class="h-5 p-0"
x-on:click="invoice.address_id = 0; selected_address = {}"/>
</x-danger-button>
</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>
<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="invoice.address_id == 0"
x-on:click="setInvoiceAddress();">
<template x-for="addr in addresses[invoice.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="selected_address.name"></div>
<div x-text="selected_address.address"></div>
<div class="flex flex-row">
<div class="mr-2" x-text="selected_address.zip"></div>
<div x-text="selected_address.city"></div>
</div>
<div x-text="selected_address.phone"></div>
<div x-text="selected_address.email"></div>
</div>
<!-- Invoice delivery -->
<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="invoice.delivery_id != null">
<x-trash-icon class="h-5 p-0"
x-on:click="invoice.delivery_id = null; selected_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="invoice.delivery_id == null"
x-on:click="setDeliveryAddress()">
<template x-for="addr in addresses[invoice.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="selected_delivery.name"></div>
<div x-text="selected_delivery.address"></div>
<div class="flex flex-row">
<div class="mr-2" x-text="selected_delivery.zip"></div>
<div x-text="selected_delivery.city"></div>
</div>
<div x-text="selected_delivery.phone"></div>
<div x-text="selected_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">
<!-- Invoice options -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg"
x-show="Object.keys(selected_address).length > 0">
<div class="max-w">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('invoice.Invoice items') }}
{{ __('invoice.Invoice options') }}
</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.") }}
{{ __("invoice.Select the options for this invoice.") }}
</p>
</header>
<div class="flex flex-row gap-4">
<!-- 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"/>
<div class="flex flex-col w-1/2">
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="type" :value="__('invoice.Type code')"/>
<select
class="w-2/3 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="type" x-model="invoice.type">
<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"/>
@foreach(App\Enum\InvoiceTypeCode::options() as $key => $value)
<option value="{{ $key }}">{{ $value }}</option>
@endforeach
<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>
</div>
<div class="flex flex-row items-center"
x-show="!from_project && (invoice.type === '326' || invoice.type === '875')">
<x-input-label class="w-1/3" for="project_count"
:value="__('invoice.Partial invoice number')"/>
<x-text-input id="project_count" name="project_count" type="text"
class="mt-1 w-2/3"
autofocus
x-model="invoice.project_count"/>
<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>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="due_date"
:value="__('invoice.Payment terms')"/>
<x-text-input id="due_date" name="due_date" type="date"
class="mt-1 w-2/3"
autofocus
x-model="invoice.due_date"/>
<div class="w-1/12 relative h-10">
<x-primary-button x-on:click="addItem();" class="absolute right-0">+</x-primary-button>
</div>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="cash_discount"
:value="__('invoice.Cash discount')"/>
<x-text-input id="cash_discount" name="cash_discount" type="number" steps="0.01"
class="mt-1 w-2/3"
autofocus x-on:blur="calculateSum();"
x-model="invoice.cash_discount"/>
</div>
<div class="flex flex-row items-center" x-show="invoice.cash_discount != 0">
<x-input-label class="w-1/3" for="cash_discount_date"
:value="__('invoice.Cash discount until')"/>
<x-text-input id="cash_discount_date" name="cash_discount_date" type="date"
class="mt-1 w-2/3"
autofocus
x-model="invoice.cash_discount_date"/>
</div>
</div>
<div class="flex flex-col w-1/2">
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="net" :value="__('invoice.Net')"/>
<x-text-input id="net" name="net" type="number"
class="mt-1 w-2/3" step="0.01"
autofocus disabled
x-model="net"/>
</div>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="tax" :value="__('invoice.Tax')"/>
<x-text-input id="tax" name="tax" type="number"
class="mt-1 w-2/3" step="0.01"
autofocus disabled
x-model="invoice.tax"/>
</div>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="sum" :value="__('invoice.Sum')"/>
<x-text-input id="sum" name="sum" type="number"
class="mt-1 w-2/3" step="0.01"
autofocus disabled
x-model="invoice.sum"/>
</div>
<div class="flex flex-row items-center" x-show="invoice.cash_discount != 0">
<x-input-label class="w-1/3" for="discount" :value="__('invoice.Cash discount sum')"/>
<x-text-input id="discount" name="discount" type="number"
class="mt-1 w-2/3" step="0.01"
autofocus disabled
x-model="discount"/>
</div>
</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>
</section>
</div>
</div>
<!-- Invoice items -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg"
x-show="Object.keys(selected_address).length > 0">
<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>
<!-- Existing invoice items -->
<div x-sort="handle" x-bind="items" class="mt-4">
<div x-sort="sortItems" x-bind="items">
<template x-for="(item, index) in items">
<div x-sort:item="index">
<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
autofocus x-on:blur="calculateItem(index);"
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"
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="text"
<x-text-input id="items[index].price" name="items[index].price" type="number"
class="mt-1 block w-1/12"
autofocus
autofocus x-on:blur="calculateItem(index);"
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-on:blur="calculateItem(index);"
x-model="items[index].discount"/>
<select name="items[index].tax" x-model="items[index].tax"
x-on:change="calculateItem(index);"
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"
@@ -205,6 +364,11 @@
</template>
</select>
<x-text-input id="items[index].total" name="items[index].total" type="number"
class="mt-1 block w-1/12"
autofocus disabled
x-model="items[index].total"/>
<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">
@@ -213,31 +377,35 @@
</div>
</div>
<div class="flex flex-row items-end gap-2 w-full">
<div class="flex flex-row gap-2">
<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 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"
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>
<x-primary-button x-on:click="submitForm();" class="">{{ __('form.Save') }}</x-primary-button>
</div>
</div>
<x-primary-button x-on:click="submit();" class="">{{ __('form.Save') }}</x-primary-button>
</div>
</div>
</x-app-layout>
<script>
@@ -245,51 +413,50 @@
function invoiceForm() {
return {
card: {},
customers: {},
customer: {},
customer_id: 0,
addresses: {},
address_id: 0,
address: {},
delivery_id: null,
delivery: {},
tax_rates: {},
tax_standard: 0,
invoice_item: {},
invoice_id: 0,
from_project: false,
error: false,
message: '',
customer_search: '',
project_search: '',
projects: [],
customers: [],
tax_rates: [],
addresses: [],
items: [],
sort: [],
selection: '',
selected_project: {},
selected_customer: {},
selected_address: {},
selected_delivery: {},
net: 0,
discount: 0,
search: '',
error: false,
message: '',
invoice: {
customer_id: 0,
address_id: 0,
delivery_id: null,
type: '380',
currency_code: 'EUR',
tax: 0,
sum: 0,
project_id: null,
due_date: '{!! \Illuminate\Support\Carbon::today()->addDays(intval($options->payment_terms))->format('Y-m-d') !!}',
cash_discount: 0,
cash_discount_date: '{!! \Illuminate\Support\Carbon::today()->addDays(intval($options->cash_discount_date))->format('Y-m-d') !!}',
},
init() {
this.getCustomers();
this.getProjects();
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;
@@ -300,79 +467,123 @@
})
},
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();
})
},
getFilteredCustomer() {
if (this.search === '') {
if (this.customer_search === '') {
return this.customers;
}
return this.customers.filter((customer) => {
return customer.name
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
.includes(this.customer_search.replace(/ /g, '').toLowerCase())
||
customer.email
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
.includes(this.customer_search.replace(/ /g, '').toLowerCase())
||
customer.street
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
.includes(this.customer_search.replace(/ /g, '').toLowerCase())
||
customer.city
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
.includes(this.customer_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;
getProjects() {
let vm = this;
axios.get('project')
.then(function (response) {
vm.projects = response.data;
})
.catch(function (error) {
vm.error = true;
vm.message = error.response.data.message;
})
},
getFilteredProject() {
if (this.project_search === '') {
return this.projects;
}
if (this.customer.delivery) {
this.delivery_id = this.customer.delivery.id;
this.delivery = this.customer.delivery;
}
this.getCustomerAddresses();
return this.projects.filter((project) => {
return project.name
.replace(/ /g, '')
.toLowerCase()
.includes(this.project_search.replace(/ /g, '').toLowerCase())
||
project.project_number
.replace(/ /g, '')
.toLowerCase()
.includes(this.project_search.replace(/ /g, '').toLowerCase())
||
project.customer_name
.replace(/ /g, '')
.toLowerCase()
.includes(this.project_search.replace(/ /g, '').toLowerCase())
||
project.customer_email
.replace(/ /g, '')
.toLowerCase()
.includes(this.project_search.replace(/ /g, '').toLowerCase())
})
},
getTaxRates() {
let vm = this;
axios.get('/taxrate')
.then(function (response) {
vm.tax_rates = response.data;
})
},
getCustomerAddresses() {
if (typeof (this.addresses[this.customer_id]) == 'undefined') {
if (typeof (this.addresses[this.invoice.customer_id]) == 'undefined') {
let vm = this;
axios.get('/customer/' + this.customer_id + '/address')
axios.get('/customer/' + this.invoice.customer_id + '/address')
.then(function (response) {
vm.addresses[vm.customer_id] = response.data;
vm.addresses[vm.invoice.customer_id] = response.data;
})
}
},
setInvoiceAddress() {
let id = document.querySelector('#select_address_id').value;
if (id === '') {
return;
}
this.invoice.address_id = id;
let address_key = Object.keys(this.addresses[this.invoice.customer_id]).find(key => (this.addresses[this.invoice.customer_id][key].id == id));
this.selected_address = this.addresses[this.invoice.customer_id][address_key];
},
setDeliveryAddress() {
let id = document.querySelector('#select_delivery_id').value;
if (id === '') {
return;
}
this.invoice.delivery_id = id;
let address_key = Object.keys(this.addresses[this.invoice.customer_id]).find(key => (this.addresses[this.invoice.customer_id][key].id == id));
this.selected_delivery = this.addresses[this.invoice.customer_id][address_key];
},
addItem() {
this.items.push(this.invoice_item);
let item = {
name: '',
article_number: '',
description: '',
amount: 1,
discount: 0,
tax: 19,
price: 0,
total: 0
};
this.items.push(item);
this.sort.push(this.items.length - 1);
this.invoice_item = this.newItem();
},
deleteItem(index) {
@@ -386,65 +597,26 @@
}
},
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.post('invoice', {
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);
})
calculateItem(index) {
this.items[index].total = (parseFloat(this.items[index].amount) * parseFloat(this.items[index].price) * (1 + parseFloat(this.items[index].tax) / 100) * (1 - parseFloat(this.items[index].discount) / 100)).toFixed(2);
this.calculateSum();
},
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];
calculateSum() {
let vm = this;
vm.invoice.tax = 0;
vm.invoice.sum = 0;
this.items.forEach(function (item) {
vm.invoice.tax += item.amount * item.price * (item.tax / 100) * (1 - item.discount / 100);
vm.invoice.sum += item.total * 1;
});
vm.invoice.tax = (vm.invoice.tax).toFixed(2)
vm.invoice.sum = (vm.invoice.sum).toFixed(2)
vm.net = (vm.invoice.sum - vm.invoice.tax).toFixed(2);
vm.discount = (vm.invoice.sum * (100 - vm.invoice.cash_discount) / 100).toFixed(2);
},
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) {
sortItems(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]) {
@@ -464,6 +636,39 @@
}
},
async submit() {
let sort_flipped = Object.entries(this.sort)
.reduce((obj, [key, value]) => ({...obj, [value]: key}), {});
const promises = [];
const main = axios.post('/invoice', this.invoice)
.then(function (response) {
self.invoice_id = response.data.id;
})
.catch(function (error) {
self.error = true;
self.message = error.response.data.message;
})
promises.push(main);
await Promise.all(promises);
for (let i = 0; i < self.items.length; i++) {
let pos = sort_flipped[i];
let item = self.items[pos];
const result = axios.post('invoice/' + self.invoice_id + '/item', item)
.catch(function (error) {
self.error = true;
self.message = error.response.data.message;
})
promises.push(result);
}
await Promise.all(promises);
if (!self.error) {
window.location.href = '/invoice/' + self.invoice_id;
}
}
}
}
</script>

View File

@@ -9,34 +9,67 @@
<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 -->
<!-- Create invoice for customer or project -->
<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 class="grid grid-cols-2 gap-12">
<div>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('invoice.Select invoice basis') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("invoice.Should this invoice be created for customer or project?") }}
</p>
</div>
<label for="toggleInfo"
class="inline-flex min-w-[14rem] cursor-pointer items-center justify-between gap-3 rounded-md border border-neutral-300 bg-neutral-50 px-4 py-1.5 dark:border-neutral-700 dark:bg-neutral-900">
<input id="toggleInfo" type="checkbox" class="peer sr-only" role="switch"
:checked="from_project" x-model="from_project"
x-on:change="invoice.customer_id = 0; selected_customer = {}; invoice.project_id = 0; selected_project = {};"/>
<span
class="trancking-wide text-sm font-medium text-neutral-600 peer-checked:text-neutral-900 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-neutral-300 dark:peer-checked:text-white">{{ __('customer.Customer') }}</span>
<div
class="relative h-6 w-11 after:h-5 after:w-5 peer-checked:after:translate-x-5 rounded-full border border-neutral-300 bg-neutral-50 after:absolute after:bottom-0 after:left-[0.0625rem] after:top-0 after:my-auto after:rounded-full after:bg-neutral-600 after:transition-all after:content-[''] peer-checked:bg-sky-500 peer-checked:after:bg-white peer-focus:outline peer-focus:outline-2 peer-focus:outline-offset-2 peer-focus:outline-neutral-800 peer-focus:peer-checked:outline-sky-500 peer-active:outline-offset-0 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:border-neutral-700 dark:bg-neutral-900 dark:after:bg-neutral-300 dark:peer-checked:bg-sky-500 dark:peer-checked:after:bg-white dark:peer-focus:outline-neutral-300 dark:peer-focus:peer-checked:outline-sky-500"
aria-hidden="true"></div>
<span
class="trancking-wide text-sm font-medium text-neutral-600 peer-checked:text-neutral-900 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-neutral-300 dark:peer-checked:text-white">{{ __('project.Project') }}</span>
</label>
</header>
</section>
</div>
</div>
<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>
<!-- Customer data -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-show="!from_project">
<div class="max-w">
<section>
<header class="grid grid-cols-2 gap-12 pb-8">
<div>
<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>
</div>
<div class="flex flex-row items-center gap-8">
<x-input-label for="search_customer" :value="__('common.Search')"/>
<x-text-input id="search_customer" name="search_customer" type="text"
class="mt-1 block w-full"
x-ref="search_customer"
autofocus
placeholder="{{ __('invoice.Search customer') }}"
x-on:keydown.window.prevent.slash="$refs.search_customer.focus()"
x-model="customer_search"/>
</div>
</header>
<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);">
:class="customer.id == invoice.customer_id ? 'font-bold' : ''"
@click="invoice.customer_id = customer.id; selected_customer = customer; getCustomerAddresses(); if(customer.address != null) { invoice.address_id = customer.address.id; selected_address = customer.address; } else { invoice.address_id = 0; selected_address = {}; } if(customer.delivery != null) { invoice.delivery_id = customer.delivery.id; selected_delivery = customer.delivery; } else { invoice.delivery_id = null; selected_delivery = {}; }">
<div x-text="customer.name"></div>
<div x-text="customer.email"></div>
<div x-text="(customer.address) ? customer.address.address : ''"></div>
@@ -46,12 +79,54 @@
</div>
</section>
</div>
</div>
<!-- Project data -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-show="from_project">
<div class="max-w">
<section>
<header class="grid grid-cols-2 gap-12 pb-8">
<div>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('invoice.Select project') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("invoice.Select your project") }}
</p>
</div>
<div class="flex flex-row gap-8 items-center">
<x-input-label for="search_project" :value="__('common.Search')"/>
<x-text-input id="search_project" name="search_project" type="text"
class="mt-1 block w-full"
x-ref="search_project"
autofocus
placeholder="{{ __('invoice.Search project') }}"
x-on:keydown.window.prevent.slash="$refs.search_project.focus()"
x-model="project_search"/>
</div>
</header>
<div>
<template x-for="(project, index) in getFilteredProject()">
<div class="cursor-pointer grid grid-cols-4 even:bg-gray-100 odd:bg-white"
:class="project.id == invoice.project_id ? 'font-bold' : ''"
@click="invoice.project_id = project.id; selected_project = project; invoice.customer_id = project.customer.id; selected_customer = project.customer; getCustomerAddresses();">
<div x-text="project.name"></div>
<div x-text="project.project_number"></div>
<div x-text="project.customer_name"></div>
<div x-text="project.customer_email"></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="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg"
x-show="Object.keys(selected_customer).length > 0">
<div class="max-w">
<section>
<header>
@@ -64,141 +139,229 @@
</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>
<!-- Invoice address -->
<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="invoice.address_id != 0">
<x-trash-icon class="h-5 p-0"
x-on:click="invoice.address_id = 0; selected_address = {}"/>
</x-danger-button>
</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>
<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="invoice.address_id == 0"
x-on:click="setInvoiceAddress();">
<template x-for="addr in addresses[invoice.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="selected_address.name"></div>
<div x-text="selected_address.address"></div>
<div class="flex flex-row">
<div class="mr-2" x-text="selected_address.zip"></div>
<div x-text="selected_address.city"></div>
</div>
<div x-text="selected_address.phone"></div>
<div x-text="selected_address.email"></div>
</div>
<!-- Invoice delivery -->
<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="invoice.delivery_id != null">
<x-trash-icon class="h-5 p-0"
x-on:click="invoice.delivery_id = null; selected_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="invoice.delivery_id == null"
x-on:click="setDeliveryAddress()">
<template x-for="addr in addresses[invoice.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="selected_delivery.name"></div>
<div x-text="selected_delivery.address"></div>
<div class="flex flex-row">
<div class="mr-2" x-text="selected_delivery.zip"></div>
<div x-text="selected_delivery.city"></div>
</div>
<div x-text="selected_delivery.phone"></div>
<div x-text="selected_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">
<!-- Invoice options -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg"
x-show="Object.keys(selected_address).length > 0">
<div class="max-w">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('invoice.Invoice items') }}
{{ __('invoice.Invoice options') }}
</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.") }}
{{ __("invoice.Select the options for this invoice.") }}
</p>
</header>
<div class="flex flex-row gap-4">
<!-- 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"/>
<div class="flex flex-col w-1/2">
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="type" :value="__('invoice.Type code')"/>
<select
class="w-2/3 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="type" x-model="invoice.type">
<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"/>
@foreach(App\Enum\InvoiceTypeCode::options() as $key => $value)
<option value="{{ $key }}">{{ $value }}</option>
@endforeach
<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>
</div>
<div class="flex flex-row items-center"
x-show="!from_project && (invoice.type === '326' || invoice.type === '875')">
<x-input-label class="w-1/3" for="project_count"
:value="__('invoice.Partial invoice number')"/>
<x-text-input id="project_count" name="project_count" type="text"
class="mt-1 w-2/3"
autofocus
x-model="invoice.project_count"/>
<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>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="due_date"
:value="__('invoice.Payment terms')"/>
<x-text-input id="due_date" name="due_date" type="date"
class="mt-1 w-2/3"
autofocus
x-model="invoice.due_date"/>
<div class="w-1/12 relative h-10">
<x-primary-button x-on:click="addItem();" class="absolute right-0">+</x-primary-button>
</div>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="cash_discount"
:value="__('invoice.Cash discount')"/>
<x-text-input id="cash_discount" name="cash_discount" type="number" steps="0.01"
class="mt-1 w-2/3"
autofocus x-on:blur="calculateSum();"
x-model="invoice.cash_discount"/>
</div>
<div class="flex flex-row items-center" x-show="invoice.cash_discount != 0">
<x-input-label class="w-1/3" for="cash_discount_date"
:value="__('invoice.Cash discount until')"/>
<x-text-input id="cash_discount_date" name="cash_discount_date" type="date"
class="mt-1 w-2/3"
autofocus
x-model="invoice.cash_discount_date"/>
</div>
</div>
<div class="flex flex-col w-1/2">
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="net" :value="__('invoice.Net')"/>
<x-text-input id="net" name="net" type="number"
class="mt-1 w-2/3" step="0.01"
autofocus disabled
x-model="net"/>
</div>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="tax" :value="__('invoice.Tax')"/>
<x-text-input id="tax" name="tax" type="number"
class="mt-1 w-2/3" step="0.01"
autofocus disabled
x-model="invoice.tax"/>
</div>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="sum" :value="__('invoice.Sum')"/>
<x-text-input id="sum" name="sum" type="number"
class="mt-1 w-2/3" step="0.01"
autofocus disabled
x-model="invoice.sum"/>
</div>
<div class="flex flex-row items-center" x-show="invoice.cash_discount != 0">
<x-input-label class="w-1/3" for="discount" :value="__('invoice.Cash discount sum')"/>
<x-text-input id="discount" name="discount" type="number"
class="mt-1 w-2/3" step="0.01"
autofocus disabled
x-model="discount"/>
</div>
</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>
</section>
</div>
</div>
<!-- Invoice items -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg"
x-show="Object.keys(selected_address).length > 0">
<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>
<!-- Existing invoice items -->
<div x-sort="handle" x-bind="items" class="mt-4">
<div x-sort="sortItems">
<template x-for="(item, index) in items">
<div >
<div x-sort:item="index">
<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"
<x-text-input id="items[index].amount" name="items[index].amount"
type="number"
class="mt-1 block w-1/12"
autofocus
autofocus x-on:blur="calculateItem(index);"
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"
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="text"
<x-text-input id="items[index].price" name="items[index].price"
type="number"
class="mt-1 block w-1/12"
autofocus
autofocus x-on:blur="calculateItem(index);"
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-on:blur="calculateItem(index);"
x-model="items[index].discount"/>
<select name="items[index].tax" x-model="items[index].tax"
x-on:change="calculateItem(index);"
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"
@@ -206,36 +369,48 @@
</template>
</select>
<x-text-input id="items[index].total" name="items[index].total"
type="number"
class="mt-1 block w-1/12"
autofocus disabled
x-model="items[index].total"/>
<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 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="flex flex-row gap-2">
<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 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"
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>
<x-primary-button x-on:click="submitForm();" class="">{{ __('form.Save') }}</x-primary-button>
</div>
</div>
<x-primary-button x-on:click="submit();" class="">{{ __('form.Save') }}</x-primary-button>
</div>
</div>
@else
@@ -250,60 +425,48 @@
</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: '',
from_project: {!! ($invoice->project_id == null) ? 'false' : 'true' !!},
error: false,
message: '',
customer_search: '',
project_search: '',
projects: [],
customers: [],
tax_rates: [],
addresses: [],
items: {!! $invoice->items !!},
sort: [],
selected_project: {!! ($invoice->project) ? $invoice->project : '{}' !!},
selected_customer: {!! $invoice->customer !!},
selected_address: {!! $invoice->address !!},
selected_delivery: {!! ($invoice->delivery) ? $invoice->delivery : '{}' !!},
net: 0,
discount: 0,
invoice: {!! $invoice !!},
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,
this.addresses[this.invoice.customer_id] = {!! $invoice->customer->addresses !!};
for (let i = 0; i < this.items.length; i++) {
this.sort.push(i);
}
this.getCustomers();
this.getProjects();
this.getTaxRates();
this.calculateSum();
self = this;
},
getCustomers() {
let vm = this;
axios.get('/customer')
.then(function (response) {
vm.customers = response.data;
@@ -314,85 +477,123 @@
})
},
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 === '') {
if (this.customer_search === '') {
return this.customers;
}
return this.customers.filter((customer) => {
return customer.name
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
.includes(this.customer_search.replace(/ /g, '').toLowerCase())
||
customer.email
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
.includes(this.customer_search.replace(/ /g, '').toLowerCase())
||
customer.street
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
.includes(this.customer_search.replace(/ /g, '').toLowerCase())
||
customer.city
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
.includes(this.customer_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;
getProjects() {
let vm = this;
axios.get('project')
.then(function (response) {
vm.projects = response.data;
})
.catch(function (error) {
vm.error = true;
vm.message = error.response.data.message;
})
},
getFilteredProject() {
if (this.project_search === '') {
return this.projects;
}
if (this.customer.delivery) {
this.delivery_id = this.customer.delivery.id;
this.delivery = this.customer.delivery;
}
this.getCustomerAddresses();
return this.projects.filter((project) => {
return project.name
.replace(/ /g, '')
.toLowerCase()
.includes(this.project_search.replace(/ /g, '').toLowerCase())
||
project.project_number
.replace(/ /g, '')
.toLowerCase()
.includes(this.project_search.replace(/ /g, '').toLowerCase())
||
project.customer_name
.replace(/ /g, '')
.toLowerCase()
.includes(this.project_search.replace(/ /g, '').toLowerCase())
||
project.customer_email
.replace(/ /g, '')
.toLowerCase()
.includes(this.project_search.replace(/ /g, '').toLowerCase())
})
},
getTaxRates() {
let vm = this;
axios.get('/taxrate')
.then(function (response) {
vm.tax_rates = response.data;
})
},
getCustomerAddresses() {
if (typeof (this.addresses[this.customer_id]) == 'undefined') {
if (typeof (this.addresses[this.invoice.customer_id]) == 'undefined') {
let vm = this;
axios.get('/customer/' + this.customer_id + '/address')
axios.get('/customer/' + this.invoice.customer_id + '/address')
.then(function (response) {
vm.addresses[vm.customer_id] = response.data;
vm.addresses[vm.invoice.customer_id] = response.data;
})
}
},
setInvoiceAddress() {
let id = document.querySelector('#select_address_id').value;
if (id === '') {
return;
}
this.invoice.address_id = id;
let address_key = Object.keys(this.addresses[this.invoice.customer_id]).find(key => (this.addresses[this.invoice.customer_id][key].id == id));
this.selected_address = this.addresses[this.invoice.customer_id][address_key];
},
setDeliveryAddress() {
let id = document.querySelector('#select_delivery_id').value;
if (id === '') {
return;
}
this.invoice.delivery_id = id;
let address_key = Object.keys(this.addresses[this.invoice.customer_id]).find(key => (this.addresses[this.invoice.customer_id][key].id == id));
this.selected_delivery = this.addresses[this.invoice.customer_id][address_key];
},
addItem() {
this.items.push(this.invoice_item);
let item = {
name: '',
article_number: '',
description: '',
amount: 1,
discount: 0,
tax: 19,
price: 0,
total: 0
};
this.items.push(item);
this.sort.push(this.items.length - 1);
this.invoice_item = this.newItem();
},
deleteItem(index) {
@@ -406,64 +607,26 @@
}
},
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;
}
})
.catch(function (error) {
console.log(error);
})
calculateItem(index) {
this.items[index].total = (parseFloat(this.items[index].amount) * parseFloat(this.items[index].price) * (1 + parseFloat(this.items[index].tax) / 100) * (1 - parseFloat(this.items[index].discount / 100))).toFixed(2);
this.calculateSum();
},
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];
calculateSum() {
let vm = this;
vm.invoice.tax = 0;
vm.invoice.sum = 0;
this.items.forEach(function (item) {
vm.invoice.tax += item.amount * item.price * (item.tax / 100) * (1 - item.discount / 100);
vm.invoice.sum += item.total * 1;
});
vm.invoice.tax = (vm.invoice.tax).toFixed(2)
vm.invoice.sum = (vm.invoice.sum).toFixed(2)
vm.net = (vm.invoice.sum - vm.invoice.tax).toFixed(2);
vm.discount = (vm.invoice.sum * (100 - vm.invoice.cash_discount) / 100).toFixed(2);
},
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) {
sortItems(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]) {
@@ -483,6 +646,36 @@
}
},
async submit() {
let sort_flipped = Object.entries(this.sort)
.reduce((obj, [key, value]) => ({...obj, [value]: key}), {});
const promises = [];
const main = axios.put('/invoice/' + this.invoice.id, this.invoice)
.catch(function (error) {
self.error = true;
self.message = error.response.data.message;
})
promises.push(main);
await Promise.all(promises);
for (let i = 0; i < self.items.length; i++) {
let pos = sort_flipped[i];
let item = self.items[pos];
const result = axios.post('invoice/' + self.invoice.id + '/item', item)
.catch(function (error) {
self.error = true;
self.message = error.response.data.message;
})
promises.push(result);
}
await Promise.all(promises);
if (!self.error) {
window.location.href = '/invoice/' + self.invoice.id;
}
}
}
}
</script>

View File

@@ -5,8 +5,10 @@
{{ __('invoice.Invoice') }} {{ $invoice->number }} ({{ $invoice->localized_state }})
</h2>
<p class="relative flex flex-row">
<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-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')"/>
</p>
</div>
</x-slot>
@@ -20,11 +22,14 @@
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.Customer') }}: {{ $invoice->customer->name }} ({{ $invoice->customer->email }})
{{ __('customer.Customer') }}: {{ $invoice->customer->name }}
({{ $invoice->customer->email }})
</h2>
<div class="grid grid-cols-2 mt-1 text-sm text-gray-600 dark:text-gray-400">
<div class="grid grid-cols-2 mt-4 text-sm text-gray-600 dark:text-gray-400">
<div>{{ __("customer.Invoice Address") }}</div>
<div>{{ __("customer.Delivery Address") }}</div>
@if($invoice->delivery)
<div>{{ __("customer.Delivery Address") }}</div>
@endif
</div>
</header>
@@ -42,6 +47,95 @@
</div>
</div>
<!-- Invoice options -->
<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 options') }}
</h2>
</header>
</section>
<div class="flex flex-col gap-2 w-1/2 mt-4 text-sm text-gray-600 dark:text-gray-400">
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="type" :value="__('invoice.Type code')"/>
<x-text-input id="type" name="type" type="text"
value="{{ \App\Enum\InvoiceTypeCode::label($invoice->type) }}"
class="mt-1 w-2/3"
autofocus disabled
/>
</div>
@if(in_array($invoice->type, ['326', '875']))
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="project_count"
:value="__('invoice.Partial invoice number')"/>
<x-text-input id="project_count" name="project_count" type="text"
value="{{ $invoice->project_count }}"
class="mt-1 w-2/3"
autofocus disabled
/>
</div>
@endif
@if($invoice->project)
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="project_count"
:value="__('project.Project')"/>
<x-text-input id="project_count" name="project_count" type="text"
value="{{ $invoice->project->name }}"
class="mt-1 w-2/3"
autofocus disabled
/>
</div>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="project_count"
:value="__('project.Project Number')"/>
<x-text-input id="project_count" name="project_count" type="text"
value="{{ $invoice->project->project_number }}"
class="mt-1 w-2/3"
autofocus disabled
/>
</div>
@endif
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="due_date"
:value="__('invoice.Payment terms')"/>
<x-text-input id="due_date" name="due_date" type="date"
value="{{ $invoice->due_date }}"
class="mt-1 w-2/3"
autofocus disabled
/>
</div>
<div class="flex flex-row items-center">
<x-input-label class="w-1/3" for="cash_discount"
:value="__('invoice.Cash discount')"/>
<x-text-input id="cash_discount" name="cash_discount" type="number" steps="0.01"
value="{{ $invoice->cash_discount }}"
class="mt-1 w-2/3"
autofocus disabled
/>
</div>
<div class="flex flex-row items-center" x-show="invoice.cash_discount != 0">
<x-input-label class="w-1/3" for="cash_discount_date"
:value="__('invoice.Cash discount until')"/>
<x-text-input id="cash_discount_date" name="cash_discount_date" type="date"
value="{{ $invoice->cash_discount_date }}"
class="mt-1 w-2/3"
autofocus disabled
/>
</div>
</div>
</div>
</div>
<!-- Invoice items -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="max-w">
@@ -50,12 +144,18 @@
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('invoice.Invoice items') }}
</h2>
<div class="flex flex-row items-end gap-2 w-full mt-1 text-sm text-gray-600 dark:text-gray-400">
<div
class="flex flex-row items-end gap-2 w-full mt-4 text-sm text-gray-600 dark:text-gray-400">
<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 text-right"/>
<x-input-label for="invoice_item.tax" :value="__('invoice.Tax')" class="w-1/12 text-right"/>
<x-input-label for="invoice_item.tax" :value="__('invoice.Sum')" class="w-1/12 text-right"/>
<x-input-label for="invoice_item.name" :value="__('invoice.Name')" class="w-7/12"/>
<x-input-label for="invoice_item.price" :value="__('invoice.Price')"
class="w-1/12 text-right"/>
<x-input-label for="invoice_item.tax" :value="__('invoice.Discount')"
class="w-1/12 text-right"/>
<x-input-label for="invoice_item.tax" :value="__('invoice.Tax')"
class="w-1/12 text-right"/>
<x-input-label for="invoice_item.tax" :value="__('invoice.Sum')"
class="w-1/12 text-right"/>
</div>
</header>
@@ -75,7 +175,8 @@
<div class="flex flex-row items-end gap-2 w-full">
<div class="w-1/12"></div>
<div class="mt-1 block w-2/3">{{ __('invoice.Net') }}</div>
<div class="w-1/4 text-right">{{ \Illuminate\Support\Number::currency($invoice->sum - $invoice->tax) }}</div>
<div
class="w-1/4 text-right">{{ \Illuminate\Support\Number::currency($invoice->sum - $invoice->tax) }}</div>
</div>
<div class="flex flex-row items-end gap-2 w-full">
<div class="w-1/12"></div>
@@ -87,6 +188,11 @@
<div class="mt-1 block w-2/3">{{ __('invoice.Sum') }}</div>
<div class="w-1/4 text-right">{{ \Illuminate\Support\Number::currency($invoice->sum) }}</div>
</div>
<div class="flex flex-row items-end gap-2 w-full">
<div class="w-1/12"></div>
<div class="mt-1 block w-2/3">{{ __('invoice.Cash discount sum') }}</div>
<div class="w-1/4 text-right">{{ \Illuminate\Support\Number::currency($invoice->sum * (100 - $invoice->cash_discount) / 100) }}</div>
</div>
</div>
</div>
@@ -94,8 +200,12 @@
<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>
<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>