Test fixes
|
|
@ -14,7 +14,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Modules\Billing\Admin\Install;
|
||||
|
||||
use Modules\Billing\Models\SettingsEnum;
|
||||
use phpOMS\Application\ApplicationAbstract;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@
|
|||
"tax_code": "DE_M19",
|
||||
"account": "8400"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"item_code": "REDUCED",
|
||||
"account_code": "DE",
|
||||
"tax_code": "DE_M7",
|
||||
"account": "8300"
|
||||
},
|
||||
{
|
||||
"type": 2,
|
||||
"item_code": "GENERAL",
|
||||
|
|
@ -94,6 +101,20 @@
|
|||
"tax_code": "SBIZ_0",
|
||||
"account": "8195"
|
||||
},
|
||||
{
|
||||
"type": 2,
|
||||
"item_code": "REDUCED",
|
||||
"account_code": "DE",
|
||||
"tax_code": "DE_V7",
|
||||
"account": "3060"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"item_code": "REDUCED",
|
||||
"account_code": "DE_S",
|
||||
"tax_code": "SBIZ_0",
|
||||
"account": "8195"
|
||||
},
|
||||
{
|
||||
"type": 2,
|
||||
"item_code": "GENERAL",
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@
|
|||
"autoincrement": true
|
||||
},
|
||||
"billing_payment_term_code": {
|
||||
"description": "In days",
|
||||
"comment": "In days",
|
||||
"name": "billing_payment_term_code",
|
||||
"type": "VARCHAR(100)",
|
||||
"null": false,
|
||||
"unique": true
|
||||
},
|
||||
"billing_payment_term_due": {
|
||||
"description": "In days",
|
||||
"comment": "In days",
|
||||
"name": "billing_payment_term_due",
|
||||
"type": "INT",
|
||||
"null": false
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
"autoincrement": true
|
||||
},
|
||||
"billing_shipping_term_code": {
|
||||
"description": "In days",
|
||||
"comment": "In days",
|
||||
"name": "billing_shipping_term_code",
|
||||
"type": "VARCHAR(100)",
|
||||
"null": false,
|
||||
|
|
@ -338,13 +338,13 @@
|
|||
"foreignKey": "itemmgmt_attr_value_id"
|
||||
},
|
||||
"billing_tax_code": {
|
||||
"description": "tax abbr. code",
|
||||
"comment": "tax abbr. code",
|
||||
"name": "billing_tax_code",
|
||||
"type": "VARCHAR(10)",
|
||||
"null": false
|
||||
},
|
||||
"billing_tax_type": {
|
||||
"description": "sales/purchase",
|
||||
"comment": "sales/purchase",
|
||||
"name": "billing_tax_type",
|
||||
"type": "TINYINT",
|
||||
"null": false
|
||||
|
|
@ -444,7 +444,7 @@
|
|||
"null": false
|
||||
},
|
||||
"billing_type_transfer_type": {
|
||||
"description": "What kind of bill is it?",
|
||||
"comment": "What kind of bill is it?",
|
||||
"name": "billing_type_transfer_type",
|
||||
"type": "TINYINT",
|
||||
"null": false
|
||||
|
|
@ -458,25 +458,25 @@
|
|||
"foreignKey": "media_id"
|
||||
},
|
||||
"billing_type_transfer_stock": {
|
||||
"description": "Does this bill type move stock?",
|
||||
"comment": "Does this bill type move stock?",
|
||||
"name": "billing_type_transfer_stock",
|
||||
"type": "TINYINT(1)",
|
||||
"null": false
|
||||
},
|
||||
"billing_type_accounting": {
|
||||
"description": "Is this bill relevant for accounting",
|
||||
"comment": "Is this bill relevant for accounting",
|
||||
"name": "billing_type_accounting",
|
||||
"type": "TINYINT(1)",
|
||||
"null": false
|
||||
},
|
||||
"billing_type_sign": {
|
||||
"description": "1 = from->to direction, -1 = to->from direction = credit note",
|
||||
"comment": "1 = from->to direction, -1 = to->from direction = credit note",
|
||||
"name": "billing_type_sign",
|
||||
"type": "TINYINT(1)",
|
||||
"null": false
|
||||
},
|
||||
"billing_type_email": {
|
||||
"description": "send email on archive",
|
||||
"comment": "send email on archive",
|
||||
"name": "billing_type_email",
|
||||
"type": "TINYINT(1)",
|
||||
"null": false
|
||||
|
|
@ -487,7 +487,7 @@
|
|||
"null": false
|
||||
},
|
||||
"billing_type_is_template": {
|
||||
"description": "What kind of bill is it?",
|
||||
"comment": "What kind of bill is it?",
|
||||
"name": "billing_type_is_template",
|
||||
"type": "TINYINT(1)",
|
||||
"null": false
|
||||
|
|
@ -834,35 +834,35 @@
|
|||
"null": false
|
||||
},
|
||||
"billing_bill_accsegment": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_accsegment",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
"default": null
|
||||
},
|
||||
"billing_bill_accsection": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_accsection",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
"default": null
|
||||
},
|
||||
"billing_bill_accgroup": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_accgroup",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
"default": null
|
||||
},
|
||||
"billing_bill_acctype": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_acctype",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
"default": null
|
||||
},
|
||||
"billing_bill_payment": {
|
||||
"description": "should this handle the dues?",
|
||||
"comment": "should this handle the dues?",
|
||||
"name": "billing_bill_payment",
|
||||
"type": "INT",
|
||||
"null": false
|
||||
|
|
@ -911,7 +911,7 @@
|
|||
}
|
||||
},
|
||||
"billing_subscription": {
|
||||
"description": "https://learn.microsoft.com/en-us/graph/outlook-schedule-recurring-events",
|
||||
"comment": "https://learn.microsoft.com/en-us/graph/outlook-schedule-recurring-events",
|
||||
"name": "billing_subscription",
|
||||
"fields": {
|
||||
"billing_subscription_id": {
|
||||
|
|
@ -1201,7 +1201,7 @@
|
|||
"default": null
|
||||
},
|
||||
"billing_bill_element_segment": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_element_segment",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
|
|
@ -1210,7 +1210,7 @@
|
|||
"foreignKey": "itemmgmt_attr_value_id"
|
||||
},
|
||||
"billing_bill_element_section": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_element_section",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
|
|
@ -1219,7 +1219,7 @@
|
|||
"foreignKey": "itemmgmt_attr_value_id"
|
||||
},
|
||||
"billing_bill_element_salesgroup": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_element_salesgroup",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
|
|
@ -1228,7 +1228,7 @@
|
|||
"foreignKey": "itemmgmt_attr_value_id"
|
||||
},
|
||||
"billing_bill_element_productgroup": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_element_productgroup",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
|
|
@ -1237,7 +1237,7 @@
|
|||
"foreignKey": "itemmgmt_attr_value_id"
|
||||
},
|
||||
"billing_bill_element_itemtype": {
|
||||
"description": "attribute values",
|
||||
"comment": "attribute values",
|
||||
"name": "billing_bill_element_itemtype",
|
||||
"type": "INT",
|
||||
"null": true,
|
||||
|
|
@ -1323,13 +1323,13 @@
|
|||
"null": false
|
||||
},
|
||||
"billing_attr_type_required": {
|
||||
"description": "Every item must have this attribute type if set to true.",
|
||||
"comment": "Every item must have this attribute type if set to true.",
|
||||
"name": "billing_attr_type_required",
|
||||
"type": "TINYINT(1)",
|
||||
"null": false
|
||||
},
|
||||
"billing_attr_type_pattern": {
|
||||
"description": "This is a regex validation pattern.",
|
||||
"comment": "This is a regex validation pattern.",
|
||||
"name": "billing_attr_type_pattern",
|
||||
"type": "VARCHAR(255)",
|
||||
"null": false
|
||||
|
|
|
|||
|
|
@ -208,6 +208,10 @@ final class Installer extends InstallerAbstract
|
|||
$module = $app->moduleManager->get('Billing', 'ApiAttribute');
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
if (!isset($attribute['values'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$billAttrValue[$attribute['name']] = [];
|
||||
|
||||
/** @var array $value */
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ use Modules\Media\Models\NullCollection;
|
|||
use Modules\Media\Models\PathSettings;
|
||||
use Modules\Media\Models\UploadStatus;
|
||||
use Modules\Messages\Models\EmailMapper;
|
||||
use Modules\Sales\Models\NullSalesRep;
|
||||
use Modules\SupplierManagement\Models\NullSupplier;
|
||||
use Modules\SupplierManagement\Models\Supplier;
|
||||
use Modules\SupplierManagement\Models\SupplierMapper;
|
||||
|
|
@ -263,7 +264,14 @@ final class ApiBillController extends Controller
|
|||
|
||||
// Create final pdf
|
||||
$this->apiBillPdfArchiveCreate($request, $response, $data);
|
||||
$media = $response->getDataArray($request->uri->__toString())['response'];
|
||||
$media = $response->getDataArray($request->uri->__toString())['response'] ?? null;
|
||||
|
||||
if ($media === null) {
|
||||
$response->header->status = RequestStatusCode::R_400;
|
||||
$this->createInvalidUpdateResponse($request, $response, $media);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->app->eventManager->triggerSimilar('PRE:Module:' . self::NAME . '-bill-finalize', '', [
|
||||
$request->header->account,
|
||||
|
|
@ -469,7 +477,7 @@ final class ApiBillController extends Controller
|
|||
$bill->accSection = empty($temp = $bill->client->getAttribute('section')->value->id) ? null : $temp;
|
||||
$bill->accGroup = empty($temp = $bill->client->getAttribute('client_group')->value->id) ? null : $temp;
|
||||
$bill->accType = empty($temp = $bill->client->getAttribute('client_type')->value->id) ? null : $temp;
|
||||
$bill->rep = $request->getDataInt('rep') ?? $account->rep;
|
||||
$bill->rep = $request->hasData('rep') ? new NullSalesRep((int) $request->getData('rep')) : $account->rep;
|
||||
} else {
|
||||
$bill->supplier = $account;
|
||||
$bill->accTaxCode = empty($temp = $bill->supplier->getAttribute('purchase_tax_code')->value->id) ? null : $temp;
|
||||
|
|
@ -1576,11 +1584,21 @@ final class ApiBillController extends Controller
|
|||
$this->app->moduleManager->get('Editor', 'Api')->apiEditorCreate($request, $response, $data);
|
||||
|
||||
if ($response->header->status !== RequestStatusCode::R_200) {
|
||||
$response->header->status = RequestStatusCode::R_400;
|
||||
$this->createInvalidUpdateResponse($request, $response, null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Modules\Editor\Models\EditorDoc $model */
|
||||
$model = $response->getDataArray($request->uri->__toString())['response'];
|
||||
$model = $response->getDataArray($request->uri->__toString())['response'] ?? null;
|
||||
if ($model === null) {
|
||||
$response->header->status = RequestStatusCode::R_400;
|
||||
$this->createInvalidUpdateResponse($request, $response, $model);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->createModelRelation($request->header->account, $request->getDataInt('id'), $model->id, BillMapper::class, 'notes', '', $request->getOrigin());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,11 +79,11 @@ final class ApiPriceController extends Controller
|
|||
'price:' . $item->id . ':' . $client->id . ':' . $supplier->id . ':' . ($request->getDataInt('price_quantity') ?? '')
|
||||
);
|
||||
|
||||
if (!empty($json)) {
|
||||
if (!empty($json) && \is_array($json)) {
|
||||
$json['bestActualPrice'] = FloatInt::fromJson($json['bestActualPrice']);
|
||||
$json['discountPercent'] = FloatInt::fromJson($json['discountPercent']);
|
||||
$json['discountAmount'] = FloatInt::fromJson($json['discountAmount']);
|
||||
$json['bonus'] = FloatInt::fromJson($json['bonus']);
|
||||
$json['discountAmount'] = FloatInt::fromJson($json['discountAmount']);
|
||||
$json['bonus'] = FloatInt::fromJson($json['bonus']);
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,8 +159,12 @@ final class ApiPurchaseController extends Controller
|
|||
|
||||
$this->app->moduleManager->get('Billing', 'ApiBill')->apiBillCreate($billRequest, $billResponse, $data);
|
||||
|
||||
$billId = $billResponse->getDataArray('')['response']->id;
|
||||
$bills[] = $billId;
|
||||
$bill = $billResponse->getDataArray('')['response'] ?? null;
|
||||
if ($bill === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$bills[] = $bill->id;
|
||||
|
||||
// Upload and assign document to bill
|
||||
$mediaResponse = new HttpResponse();
|
||||
|
|
@ -177,14 +181,14 @@ final class ApiPurchaseController extends Controller
|
|||
$mediaRequest->setData('media', \json_encode($file));
|
||||
}
|
||||
|
||||
$mediaRequest->setData('bill', $billId);
|
||||
$mediaRequest->setData('bill', $bill->id);
|
||||
$mediaRequest->setData('tag', $tag->id);
|
||||
$mediaRequest->setData('parse_content', true, true);
|
||||
$this->app->moduleManager->get('Billing', 'ApiBill')->apiMediaAddToBill($mediaRequest, $mediaResponse, $data);
|
||||
|
||||
if (\is_array($file)) {
|
||||
/** @var \Modules\Media\Models\Media[] $uploaded */
|
||||
$uploaded = $mediaResponse->getDataArray('')['response']['upload'];
|
||||
$uploaded = $mediaResponse->getDataArray('')['response']['upload'] ?? [];
|
||||
if (empty($uploaded)) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -195,8 +199,8 @@ final class ApiPurchaseController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$request->setData('id', $billId, true);
|
||||
$request->setData('bill', $billId, true);
|
||||
$request->setData('id', $bill->id, true);
|
||||
$request->setData('bill', $bill->id, true);
|
||||
|
||||
$this->apiInvoiceParse($request, $response, $data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,6 +256,16 @@ final class BackendController extends Controller
|
|||
->where('l11n/language', $request->header->l11n->language)
|
||||
->executeGetArray();
|
||||
|
||||
$view->data['paymentterms'] = PaymentTermMapper::getAll()
|
||||
->with('l11n')
|
||||
->where('l11n/language', $request->header->l11n->language)
|
||||
->executeGetArray();
|
||||
|
||||
$view->data['shippingterms'] = ShippingTermMapper::getAll()
|
||||
->with('l11n')
|
||||
->where('l11n/language', $request->header->l11n->language)
|
||||
->executeGetArray();
|
||||
|
||||
$logs = [];
|
||||
if ($this->app->accountManager->get($request->header->account)->hasPermission(
|
||||
PermissionType::READ,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use Modules\Billing\Models\BillMapper;
|
|||
use Modules\Billing\Models\BillTypeMapper;
|
||||
use Modules\Billing\Models\InvoiceRecognition;
|
||||
use Modules\Billing\Models\NullBillType;
|
||||
use Modules\Billing\Models\SettingsEnum;
|
||||
use Modules\ItemManagement\Models\NullItem;
|
||||
use Modules\Payment\Models\PaymentType;
|
||||
use Modules\SupplierManagement\Models\NullSupplier;
|
||||
use Modules\SupplierManagement\Models\Supplier;
|
||||
|
|
@ -325,7 +325,7 @@ final class CliController extends Controller
|
|||
$internalResponse->header->l11n->language = $bill->language;
|
||||
|
||||
$this->app->moduleManager->get('ItemManagement', 'Api')->apiItemFind($internalRequest, $internalResponse);
|
||||
$item = $internalResponse->getDataArray('')[0];
|
||||
$item = $internalResponse->getDataArray('')[0] ?? new NullItem();
|
||||
|
||||
$billElement->itemName = $key;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
# Structure
|
||||
|
||||
## ER
|
||||
|
||||

|
||||
|
Before Width: | Height: | Size: 247 KiB |
5
Docs/Help/en/SUMMARY.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# User Content
|
||||
|
||||
* [Bill]({%}&page=Help/bill)
|
||||
* [Bill Types]({%}&page=Help/bill_types)
|
||||
* [Taxes]({%}&page=Help/taxes)
|
||||
37
Docs/Help/en/bill.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Bill
|
||||
|
||||
## General
|
||||
|
||||
In the overview of the bill you can define the general bill information.
|
||||
|
||||

|
||||
|
||||
In the overview of the bill you can define the general terms.
|
||||
|
||||

|
||||
|
||||
You can also select stored billing and delivery addresses from the client or supplier. Additionally, you can also create a custom billing or delivery address.
|
||||
|
||||

|
||||
|
||||
## Items
|
||||
|
||||
In the items tab you can add and modify items in the bill.
|
||||
|
||||
Depending on the settings you can select existing items by number or name or create custom invoice elements.
|
||||
|
||||
Prices, discounts and taxes are automatically calculated based on the information stored in the items, tax codes, clients and suppliers. You can overwrite these information at your own digression.
|
||||
|
||||

|
||||
|
||||
## Preview
|
||||
|
||||
In the preview tab you can preview the finished invoice. You can also change the invoice type in the preview.
|
||||
|
||||
## Archive
|
||||
|
||||
This tab only appears once the invoice is created and saved. This shows how the invoice is saved in the system.
|
||||
|
||||
## Files
|
||||
|
||||
If you want to add files to the invoice you can do this in the Files tab.
|
||||
38
Docs/Help/en/bill_types.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Bill Types
|
||||
|
||||
The Billing module provides various default bill types that can be used directly after the installation.
|
||||
|
||||
## Sales
|
||||
|
||||
| Name | Effects stock | Accounting relevant |
|
||||
| ------------------ | ------------- | ------------------- |
|
||||
| Offer | No | No |
|
||||
| Order Confirmation | No | No |
|
||||
| Delivery Note | Yes | No |
|
||||
| Invoice | No | Yes |
|
||||
| Proforma Invoice | No | No |
|
||||
| Credit Note | No | Yes |
|
||||
| Reverse Invoice | No | Yes |
|
||||
| Subscription | No | Yes |
|
||||
|
||||
## Purchase
|
||||
|
||||
| Name | Effects stock | Accounting relevant |
|
||||
| ------------------ | ------------- | ------------------- |
|
||||
| Offer | No | No |
|
||||
| Order | No | No |
|
||||
| Order Confirmation | No | No |
|
||||
| Delivery Note | Yes | No |
|
||||
| Invoice | No | Yes |
|
||||
| Proforma Invoice | No | No |
|
||||
| Credit Note | No | Yes |
|
||||
| Reverse Invoice | No | Yes |
|
||||
| Subscription | No | Yes |
|
||||
|
||||
## Warehouse
|
||||
|
||||
| Name | Effects stock | Accounting relevant |
|
||||
| -------------- | ------------- | ------------------- |
|
||||
| Stock Movement | Yes | No |
|
||||
| Stock Decrease | Yes | No |
|
||||
| Stock Increase | Yes | No |
|
||||
37
Docs/Help/en/introduction.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Introduction
|
||||
|
||||
The **Billing** module is essential for writing and managing invoices either from a sales or procurement perspective.
|
||||
|
||||
## Target Group
|
||||
|
||||
The target group for this module is the sales and purchase department.
|
||||
|
||||
# Setup
|
||||
|
||||
This module doesn't have any additional setup requirements.
|
||||
|
||||
# Features
|
||||
|
||||
## Payment Terms
|
||||
|
||||
Multiple payment terms to indicate when a invoice becomes due.
|
||||
|
||||
## Shipping Terms
|
||||
|
||||
Multiple shipping terms to indicate how the goods on the invoice are shipped.
|
||||
|
||||
### Billing Types
|
||||
|
||||
Different billing types such as:
|
||||
|
||||
* Invoice
|
||||
* Delivery note
|
||||
* Order confirmation
|
||||
* Offer
|
||||
* ... more
|
||||
|
||||
# Recommendation
|
||||
|
||||
Other modules that work great with this one together are:
|
||||
|
||||
* [SalesAnalysis]({/}?id=SalesAnalysis)
|
||||
79
Docs/Help/en/taxes.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Taxes
|
||||
|
||||
Taxes for cusstomers/suppliers and items are automatically calculated based on specific indicators/tax codes that always work in combination with each other. Visually you can think about it as a matrix where both customer/supplier indicator and item tax code result in the respective taxes.
|
||||
|
||||
**Visualization**
|
||||
|
||||
| | Item sales_tax_code 1 | Item sales_tax_code 2 | Item sales_tax_code 2 |
|
||||
| ----------------------- | --------------------- | --------------------- | --------------------- |
|
||||
| Client sales_tax_code 1 | 19 % (domestic) | 7 % (domestic) | 19 % (domestic) |
|
||||
| Client sales_tax_code 2 | 0 % (intra-community) | 0 % (intra-community) | 0 % (intra-community) |
|
||||
| Client sales_tax_code 2 | 0 % (third country) | 0 % (third country) | 0 % (third country) |
|
||||
|
||||
These indicators/tax codes are completely custom and you can define them as you see fit.
|
||||
|
||||
## Items
|
||||
|
||||
Every item that you want to sell must have an attribute set called **Sales tax code** (`sales_tax_code`).
|
||||
|
||||
Every item that you want to purchase must have an attribute set called **Purchase tax code** (`purchase_tax_code`).
|
||||
|
||||
You can define these attributes when viewing a item. For more details please refer to the [Item Management]({/}?id=ItemManagement) documentation.
|
||||
|
||||
By default the Item Management module has already defined some sales and purchase tax codes that you can use. Of course you can create additional sales and purchase tax codes as you see fit.
|
||||
|
||||
## Clients
|
||||
|
||||
Every client/customer that you want to create invoices for must have an attribute set called **Sales tax code** (`sales_tax_code`). You can define this attribute when viewing a client. For more details please refer to the [Client Management]({/}?id=ClientManagement) documentation.
|
||||
|
||||
By default the Client Management module has already defined some sales tax codes that you can use. Of course you can create additional sales tax codes as you see fit. However, the default tax codes should be already sufficient to handle your use cases.
|
||||
|
||||
### Germany
|
||||
|
||||
Most German companies only need `DE` for domestic sales, `INT` for third country sales, `EU` for intra-community sales (B2B).
|
||||
|
||||
Most small businesses only need `DE_S` for domestic sales, `INT` for third country sales, `EU_S` for intra-community sales (B2B).
|
||||
|
||||
## Suppliers
|
||||
|
||||
Every supplier that you want to create invoices for must have an attribute set called **Purchase tax code** (`purchase_tax_code`). You can define this attribute when viewing a supplier. For more details please refer to the [Supplier Management]({/}?id=SupplierManagement) documentation.
|
||||
|
||||
By default the Supplier Management module has already defined some purchase tax codes that you can use. Of course you can create additional purchase tax codes as you see fit.
|
||||
|
||||
## Tax Codes
|
||||
|
||||
Tax codes define a single code that specifies how high the taxes are. These tax codes can also be used in accounting when creating a new entry and are therefore managed by the Finance module.
|
||||
|
||||
For more details please refer to the [Finance]({/}?id=Finance) module.
|
||||
|
||||
## Tax Combinations
|
||||
|
||||
After you defined the item sales/purchase tax code and client/purchase tax code you have to create the possible combinations that can occur. The tax combinations can be viewed and edited under `Finance >> Tax Combinations`
|
||||
|
||||

|
||||
|
||||
**Example visual representation**
|
||||
|
||||
The following example still follows the same structure as mentioned in the beginning but filled with actual values.
|
||||
|
||||
| | GENERAL | REDUCED | SERVICE |
|
||||
| --- | ------------------ | ---------------- | ------------------ |
|
||||
| DE | DE_M19 (= 19% tax) | DE_M7 (= 7% tax) | DE_M19 (= 19% tax) |
|
||||
| EU | EU_S0 (= 0% tax) | EU_S0 (= 0% tax) | EU_S0 (= 0% tax) |
|
||||
| INT | S0 (= 0% tax) | S0 (= 0% tax) | S0 (= 0% tax) |
|
||||
|
||||
> Please note that despite effectively the equal tax amount (0 %) for EU and INT you probably have to use different tax codes due to your local tax laws and accounting practices.
|
||||
|
||||
> Sales and purchase combinations are separate, which means if you want to sell and purchase an item you will have to create at least one combination using sales tax codes and one using purchase tax codes.
|
||||
|
||||
### Actual combination
|
||||
|
||||

|
||||
|
||||
## Steps
|
||||
|
||||
1. Create additional item sales tax codes and purchase tax codes in the Item Management module as you see fit.
|
||||
2. Create additional sales tax codes in the Client Management module as you see fit.
|
||||
3. Create additional purchase tax codes in the Supplier Management module as you see fit.
|
||||
4. Create additional tax codes if necessary in the Finance module.
|
||||
5. Create additional tax code combinations in the Finance module as you see fit
|
||||
BIN
Docs/Help/img/bill/bill_element_list.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
Docs/Help/img/bill/bill_invoice_billing_address.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
Docs/Help/img/bill/bill_invoice_delivery_address.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
Docs/Help/img/bill/bill_invoice_general.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
Docs/Help/img/bill/bill_invoice_terms.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Docs/Help/img/taxes/taxes_combination.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
Docs/Help/img/taxes/taxes_combinations.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
38
Docs/img.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
[
|
||||
[
|
||||
"/finance/tax/combination/list",
|
||||
"//*[@id=\"content\"]/div[2]/div/section/table/tbody/tr[1]",
|
||||
"/Billing/Docs/Help/img/taxes/taxes_combinations.png"
|
||||
],
|
||||
[
|
||||
"/finance/tax/combination/view?id=1",
|
||||
"//*[@id=\"content\"]/div[2]/div/section",
|
||||
"/Billing/Docs/Help/img/taxes/taxes_combination.png"
|
||||
],
|
||||
|
||||
[
|
||||
"/sales/bill/view?id=1",
|
||||
"//*[@id=\"content\"]/div[2]/div[2]/div[1]/div/div[1]/section",
|
||||
"/Billing/Docs/Help/img/bill/bill_invoice_general.png"
|
||||
],
|
||||
[
|
||||
"/sales/bill/view?id=1",
|
||||
"//*[@id=\"content\"]//div[2]/div[2]/div[1]/div/div[1]/section[2]",
|
||||
"/Billing/Docs/Help/img/bill/bill_invoice_terms.png"
|
||||
],
|
||||
[
|
||||
"/sales/bill/view?id=1",
|
||||
"//*[@id=\"content\"]/div[2]/div[2]/div[1]/div/div[2]/section",
|
||||
"/Billing/Docs/Help/img/bill/bill_invoice_billing_address.png"
|
||||
],
|
||||
[
|
||||
"/sales/bill/view?id=1",
|
||||
"//*[@id=\"content\"]/div[2]/div[2]/div[1]/div/div[3]/section",
|
||||
"/Billing/Docs/Help/img/bill/bill_invoice_delivery_address.png"
|
||||
],
|
||||
[
|
||||
"/sales/bill/view?id=1#c-tab-2",
|
||||
"//*[@id=\"content\"]/div[2]/div[2]/div[2]/div/div/section",
|
||||
"/Billing/Docs/Help/img/bill/bill_element_list.png"
|
||||
]
|
||||
]
|
||||
|
|
@ -19,6 +19,7 @@ use Modules\Admin\Models\NullAccount;
|
|||
use Modules\ClientManagement\Models\Client;
|
||||
use Modules\Sales\Models\SalesRep;
|
||||
use Modules\SupplierManagement\Models\Supplier;
|
||||
use phpOMS\Localization\BaseStringL11nType;
|
||||
use phpOMS\Localization\ISO4217CharEnum;
|
||||
use phpOMS\Localization\ISO639x1Enum;
|
||||
use phpOMS\Stdlib\Base\FloatInt;
|
||||
|
|
@ -354,9 +355,9 @@ class Bill implements \JsonSerializable
|
|||
*/
|
||||
public int $terms = 0;
|
||||
|
||||
public ?int $paymentTerms = null;
|
||||
public ?BaseStringL11nType $paymentTerms = null;
|
||||
|
||||
public ?int $shippingTerms = null;
|
||||
public ?BaseStringL11nType $shippingTerms = null;
|
||||
|
||||
/**
|
||||
* Terms text.
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class BillMapper extends DataMapperFactory
|
|||
'billing_bill_netdiscount' => ['name' => 'billing_bill_netdiscount', 'type' => 'Serializable', 'internal' => 'netDiscount'],
|
||||
'billing_bill_taxp' => ['name' => 'billing_bill_taxp', 'type' => 'Serializable', 'internal' => 'taxP'],
|
||||
'billing_bill_fiaccount' => ['name' => 'billing_bill_fiaccount', 'type' => 'string', 'internal' => 'fiAccount'],
|
||||
'billing_bill_rep' => ['name' => 'billing_bill_rep', 'type' => 'int', 'internal' => 'rep'],
|
||||
'billing_bill_rep' => ['name' => 'billing_bill_rep', 'type' => 'int', 'internal' => 'rep'],
|
||||
'billing_bill_currency' => ['name' => 'billing_bill_currency', 'type' => 'string', 'internal' => 'currency'],
|
||||
'billing_bill_language' => ['name' => 'billing_bill_language', 'type' => 'string', 'internal' => 'language'],
|
||||
'billing_bill_referral' => ['name' => 'billing_bill_referral', 'type' => 'int', 'internal' => 'referral'],
|
||||
|
|
@ -127,6 +127,12 @@ class BillMapper extends DataMapperFactory
|
|||
'external' => 'billing_bill_note_doc',
|
||||
'self' => 'billing_bill_note_bill',
|
||||
],
|
||||
'attributes' => [
|
||||
'mapper' => BillAttributeMapper::class,
|
||||
'table' => 'billing_bill_attr',
|
||||
'self' => 'billing_bill_attr_bill',
|
||||
'external' => null,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -148,6 +154,14 @@ class BillMapper extends DataMapperFactory
|
|||
'mapper' => SalesRepMapper::class,
|
||||
'external' => 'billing_bill_rep',
|
||||
],
|
||||
'paymentTerms' => [
|
||||
'mapper' => PaymentTermMapper::class,
|
||||
'external' => 'billing_bill_paymentterms',
|
||||
],
|
||||
'shippingTerms' => [
|
||||
'mapper' => ShippingTermMapper::class,
|
||||
'external' => 'shippingTerms',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -168,14 +182,7 @@ class BillMapper extends DataMapperFactory
|
|||
'supplier' => [
|
||||
'mapper' => SupplierMapper::class,
|
||||
'external' => 'billing_bill_supplier',
|
||||
],
|
||||
'attributes' => [
|
||||
'mapper' => BillAttributeMapper::class,
|
||||
'table' => 'billing_bill_attr',
|
||||
'self' => 'billing_bill_attr_bill',
|
||||
'conditional' => true,
|
||||
'external' => null,
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ final class BillTypeMapper extends DataMapperFactory
|
|||
'billing_type_default_template' => ['name' => 'billing_type_default_template', 'type' => 'int', 'internal' => 'defaultTemplate'],
|
||||
'billing_type_transfer_stock' => ['name' => 'billing_type_transfer_stock', 'type' => 'bool', 'internal' => 'transferStock'],
|
||||
'billing_type_accounting' => ['name' => 'billing_type_accounting', 'type' => 'bool', 'internal' => 'isAccounting'],
|
||||
'billing_type_sign' => ['name' => 'billing_type_sign', 'type' => 'int', 'internal' => 'sign'],
|
||||
'billing_type_sign' => ['name' => 'billing_type_sign', 'type' => 'int', 'internal' => 'sign'],
|
||||
'billing_type_email' => ['name' => 'billing_type_email', 'type' => 'bool', 'internal' => 'email'],
|
||||
'billing_type_is_template' => ['name' => 'billing_type_is_template', 'type' => 'bool', 'internal' => 'isTemplate'],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ use phpOMS\Stdlib\Base\FloatInt;
|
|||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @template T of Bill
|
||||
* @extends BillMapper<T>
|
||||
*/
|
||||
final class PurchaseBillMapper extends BillMapper
|
||||
{
|
||||
|
|
@ -212,7 +215,7 @@ final class PurchaseBillMapper extends BillMapper
|
|||
$query->orderBy(self::TABLE . '_d1.' . self::COLUMNS[self::PRIMARYFIELD]['name'], 'DESC');
|
||||
}
|
||||
|
||||
return self::getAll()->execute($query);
|
||||
return self::getAll()->executeGetArray($query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -231,7 +234,7 @@ final class PurchaseBillMapper extends BillMapper
|
|||
$query->orderBy(self::TABLE . '_d1.' . self::COLUMNS[self::PRIMARYFIELD]['name'], 'DESC');
|
||||
}
|
||||
|
||||
return self::getAll()->execute($query);
|
||||
return self::getAll()->executeGetArray($query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ use phpOMS\Stdlib\Base\FloatInt;
|
|||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @template T of Bill
|
||||
* @extends BillMapper<T>
|
||||
*/
|
||||
final class SalesBillMapper extends BillMapper
|
||||
{
|
||||
|
|
@ -226,7 +229,7 @@ final class SalesBillMapper extends BillMapper
|
|||
$query->orderBy(self::TABLE . '_d1.' . self::COLUMNS[self::PRIMARYFIELD]['name'], 'DESC');
|
||||
}
|
||||
|
||||
return self::getAll()->execute($query);
|
||||
return self::getAll()->executeGetArray($query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -246,7 +249,7 @@ final class SalesBillMapper extends BillMapper
|
|||
$query->orderBy(self::TABLE . '_d1.' . self::COLUMNS[self::PRIMARYFIELD]['name'], 'DESC');
|
||||
}
|
||||
|
||||
return self::getAll()->execute($query);
|
||||
return self::getAll()->executeGetArray($query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -318,7 +321,7 @@ final class SalesBillMapper extends BillMapper
|
|||
->with('type/l11n')
|
||||
->sort('id', OrderType::DESC)
|
||||
->limit($limit)
|
||||
->execute($query);
|
||||
->executeGetArray($query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ use phpOMS\DataStorage\Database\Query\Builder;
|
|||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @template T of Bill
|
||||
* @extends BillMapper<T>
|
||||
*/
|
||||
final class StockBillMapper extends BillMapper
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,5 +21,5 @@ return ['Navigation' => [
|
|||
'Upload' => 'Hochladen',
|
||||
'PaymentTerms' => 'Zahlungsbedingungen',
|
||||
'ShippingTerms' => 'Lieferbedingungen',
|
||||
'TaxCombinations' => 'Tax Combinations',
|
||||
'TaxCombinations' => 'Tax Combinations',
|
||||
]];
|
||||
|
|
|
|||
|
|
@ -21,5 +21,5 @@ return ['Navigation' => [
|
|||
'Upload' => 'Upload',
|
||||
'PaymentTerms' => 'Payment Terms',
|
||||
'ShippingTerms' => 'Shipping Terms',
|
||||
'TaxCombinations' => 'Tax Combinations',
|
||||
'TaxCombinations' => 'Tax Combinations',
|
||||
]];
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ declare(strict_types=1);
|
|||
return ['Billing' => [
|
||||
'Address' => 'Adresse',
|
||||
'Addresses' => 'Adressen',
|
||||
'General' => 'Allgemein',
|
||||
'AlreadyPaid' => 'Bereits bezahlt',
|
||||
'Shipping' => 'Lieferung',
|
||||
'Terms' => 'Lieferbedingungen',
|
||||
'Amount' => 'Betrag',
|
||||
'Archive' => 'Archiev',
|
||||
'Internal' => 'Intern',
|
||||
|
|
@ -59,7 +62,6 @@ return ['Billing' => [
|
|||
'Original' => 'Original',
|
||||
'Payment' => 'Zahlung',
|
||||
'PaymentPlan' => 'Zahlungsplan',
|
||||
'Postal' => 'Post',
|
||||
'Prepaid' => 'Vorausbezahlt',
|
||||
'Preview' => 'Vorschau',
|
||||
'Price' => 'Preis',
|
||||
|
|
@ -79,7 +81,7 @@ return ['Billing' => [
|
|||
'Type' => 'Typ',
|
||||
'Types' => 'Typen',
|
||||
'Upload' => 'Hochladen',
|
||||
'Postal' => 'Postal',
|
||||
'Postal' => 'Postleitzahl',
|
||||
'Files' => 'Files',
|
||||
'TaxCode' => 'Steuerkz.',
|
||||
'PL' => 'GuV',
|
||||
|
|
|
|||
|
|
@ -15,9 +15,12 @@ declare(strict_types=1);
|
|||
return ['Billing' => [
|
||||
'Address' => 'Address',
|
||||
'Addresses' => 'Addresses',
|
||||
'General' => 'General',
|
||||
'AlreadyPaid' => 'Already Paid',
|
||||
'Amount' => 'Amount',
|
||||
'Archive' => 'Archive',
|
||||
'Shipping' => 'Shipping',
|
||||
'Terms' => 'Terms',
|
||||
'Internal' => 'Internal',
|
||||
'Error' => 'Error',
|
||||
'Billing' => 'Billing',
|
||||
|
|
@ -79,7 +82,6 @@ return ['Billing' => [
|
|||
'Type' => 'Type',
|
||||
'Types' => 'Types',
|
||||
'Upload' => 'Upload',
|
||||
'Postal' => 'Postal',
|
||||
'Files' => 'Files',
|
||||
'TaxCode' => 'Tax Code',
|
||||
'PL' => 'PL',
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ $media = $this->data['media'] ?? [];
|
|||
$bill = $this->getData('bill') ?? new NullBill();
|
||||
$elements = $bill->elements;
|
||||
|
||||
$billTypes = $this->data['billtypes'] ?? [];
|
||||
$billTypes = $this->data['billtypes'] ?? [];
|
||||
$paymentTerms = $this->data['paymentterms'] ?? [];
|
||||
$shippingTerms = $this->data['shippingterms'] ?? [];
|
||||
|
||||
$archive = $bill->getFileByTagName('internal_bill');
|
||||
|
||||
|
|
@ -75,10 +77,10 @@ echo $this->data['nav']->render(); ?>
|
|||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="tabview tab-2 col-simple">
|
||||
<div id="iBillTab" class="tabview tab-2 col-simple url-rewrite">
|
||||
<div class="box">
|
||||
<ul class="tab-links">
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('Invoice'); ?></label>
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('General'); ?></label>
|
||||
<li><label for="c-tab-2"><?= $this->getHtml('Items'); ?></label>
|
||||
<li><label for="c-tab-3"><?= $this->getHtml('Preview'); ?></label>
|
||||
<?php if (!$isNew) : ?><li><label for="c-tab-4"><?= $this->getHtml('Archive'); ?></label><?php endif; ?>
|
||||
|
|
@ -110,7 +112,7 @@ echo $this->data['nav']->render(); ?>
|
|||
<select id="iCurrency" name="bill_currency"<?= $disabled; ?>>
|
||||
<?php foreach ($currencies as $code => $currency) : $code = \substr($code, 1); ?>
|
||||
<option value="<?= $this->printHtml($code); ?>"<?= $code === $bill->currency ? ' selected' : ''; ?>><?= $this->printHtml($currency); ?>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
|
@ -163,26 +165,6 @@ echo $this->data['nav']->render(); ?>
|
|||
<input type="datetime-local" id="iDeliveryDate" name="bill_delivery_date"
|
||||
value="<?= $bill->createdAt->format('Y-m-d\TH:i'); ?>"<?= $disabled; ?>>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="iDueDate"><?= $this->getHtml('Due'); ?></label>
|
||||
<input type="datetime-local" id="iDueDate" name="bill_due"
|
||||
value="<?= (new \DateTime('now'))->format('Y-m-d\TH:i'); ?>"<?= $disabled; ?>>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="iShipment"><?= $this->getHtml('Shipment'); ?></label>
|
||||
<select id="iShipment" name="bill_shipment_type"<?= $disabled; ?>>
|
||||
<option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="iTermsOfDelivery"><?= $this->getHtml('TermsOfDelivery'); ?></label>
|
||||
<select id="iTermsOfDelivery" name="bill_termsofdelivery"<?= $disabled; ?>>
|
||||
<option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($editable) : ?>
|
||||
<div class="portlet-foot">
|
||||
|
|
@ -191,6 +173,42 @@ echo $this->data['nav']->render(); ?>
|
|||
<?php endif; ?>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="portlet">
|
||||
<div class="portlet-head"><?= $this->getHtml('Terms'); ?></div>
|
||||
<div class="portlet-body">
|
||||
<div class="form-group">
|
||||
<label for="iPaymentTerm"><?= $this->getHtml('Payment'); ?></label>
|
||||
<select id="iPaymentTerm" name="bill_payment_term"<?= $disabled; ?>>
|
||||
<?php foreach ($paymentTerms as $payment) : ?>
|
||||
<option value="<?= $payment->id; ?>"<?= $payment->id === $bill->paymentTerms ? ' selected' : ''; ?>><?= $this->printHtml($payment->getL11n()); ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="iDueDate"><?= $this->getHtml('Due'); ?></label>
|
||||
<input type="datetime-local" id="iDueDate" name="bill_due"
|
||||
value="<?= (new \DateTime('now'))->format('Y-m-d\TH:i'); ?>"<?= $disabled; ?>>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="iShippingTerm"><?= $this->getHtml('Shipping'); ?></label>
|
||||
<select id="iShippingTerm" name="bill_payment_term"<?= $disabled; ?>>
|
||||
<?php foreach ($shippingTerms as $shipping) : ?>
|
||||
<option value="<?= $shipping->id; ?>"<?= $shipping->id === $bill->shippingTerms ? ' selected' : ''; ?>><?= $this->printHtml($shipping->getL11n()); ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="iTermsOfDelivery"><?= $this->getHtml('TermsOfDelivery'); ?></label>
|
||||
<select id="iTermsOfDelivery" name="bill_termsofdelivery"<?= $disabled; ?>>
|
||||
<option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-md-6 col-lg-4">
|
||||
|
|
|
|||