mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Specify order currency (#4698)
* Add 'order_currency' to the various external order models - By default will use the currency specified for the supplier (or customer) - Can be specified per order, also * Display order currency on order pgae * Add 'order_currency' field * Enable "blank" currency option (to default to the currency specified by the referenced company * Fix default currency code when adding line items * Remove 'total_price_currency' serializer field - Now replaced with 'order_currency' for greater flexibility * Bump api_version.py * Update default order report templates * Updated docs * More docs updaes * Adjust unit tests * Use 'order_currency' in order tables * Update purchase order api unit tests
This commit is contained in:
parent
36d17c082b
commit
5fcab2aec3
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 109
|
INVENTREE_API_VERSION = 110
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
||||||
|
|
||||||
|
v110 -> 2023-04-26 : https://github.com/inventree/InvenTree/pull/4698
|
||||||
|
- Adds 'order_currency' field for PurchaseOrder / SalesOrder endpoints
|
||||||
|
|
||||||
v109 -> 2023-04-19 : https://github.com/inventree/InvenTree/pull/4636
|
v109 -> 2023-04-19 : https://github.com/inventree/InvenTree/pull/4636
|
||||||
- Adds API endpoints for the "ProjectCode" model
|
- Adds API endpoints for the "ProjectCode" model
|
||||||
|
|
||||||
|
@ -73,10 +73,17 @@ class InvenTreeCurrencySerializer(serializers.ChoiceField):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize the currency serializer"""
|
"""Initialize the currency serializer"""
|
||||||
|
|
||||||
kwargs['choices'] = currency_code_mappings()
|
choices = currency_code_mappings()
|
||||||
|
|
||||||
|
allow_blank = kwargs.get('allow_blank', False) or kwargs.get('allow_null', False)
|
||||||
|
|
||||||
|
if allow_blank:
|
||||||
|
choices = [('', '---------')] + choices
|
||||||
|
|
||||||
|
kwargs['choices'] = choices
|
||||||
|
|
||||||
if 'default' not in kwargs and 'required' not in kwargs:
|
if 'default' not in kwargs and 'required' not in kwargs:
|
||||||
kwargs['default'] = currency_code_default
|
kwargs['default'] = '' if allow_blank else currency_code_default
|
||||||
|
|
||||||
if 'label' not in kwargs:
|
if 'label' not in kwargs:
|
||||||
kwargs['label'] = _('Currency')
|
kwargs['label'] = _('Currency')
|
||||||
|
29
InvenTree/order/migrations/0093_auto_20230426_0248.py
Normal file
29
InvenTree/order/migrations/0093_auto_20230426_0248.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 3.2.18 on 2023-04-26 02:48
|
||||||
|
|
||||||
|
import InvenTree.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('order', '0092_auto_20230419_0250'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='order_currency',
|
||||||
|
field=models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='returnorder',
|
||||||
|
name='order_currency',
|
||||||
|
field=models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='order_currency',
|
||||||
|
field=models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency'),
|
||||||
|
),
|
||||||
|
]
|
@ -25,6 +25,7 @@ from mptt.models import TreeForeignKey
|
|||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
import InvenTree.ready
|
import InvenTree.ready
|
||||||
import InvenTree.tasks
|
import InvenTree.tasks
|
||||||
|
import InvenTree.validators
|
||||||
import order.validators
|
import order.validators
|
||||||
import stock.models
|
import stock.models
|
||||||
import users.models as UserModels
|
import users.models as UserModels
|
||||||
@ -69,10 +70,36 @@ class TotalPriceMixin(models.Model):
|
|||||||
help_text=_('Total price for this order')
|
help_text=_('Total price for this order')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
order_currency = models.CharField(
|
||||||
|
max_length=3,
|
||||||
|
verbose_name=_('Order Currency'),
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text=_('Currency for this order (leave blank to use company default)'),
|
||||||
|
validators=[InvenTree.validators.validate_currency_code]
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def currency(self):
|
||||||
|
"""Return the currency associated with this order instance:
|
||||||
|
|
||||||
|
- If the order_currency field is set, return that
|
||||||
|
- Otherwise, return the currency associated with the company
|
||||||
|
- Finally, return the default currency code
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.order_currency:
|
||||||
|
return self.order_currency
|
||||||
|
|
||||||
|
if self.company:
|
||||||
|
return self.company.currency_code
|
||||||
|
|
||||||
|
# Return default currency code
|
||||||
|
return currency_code_default()
|
||||||
|
|
||||||
def update_total_price(self, commit=True):
|
def update_total_price(self, commit=True):
|
||||||
"""Recalculate and save the total_price for this order"""
|
"""Recalculate and save the total_price for this order"""
|
||||||
|
|
||||||
self.total_price = self.calculate_total_price()
|
self.total_price = self.calculate_total_price(target_currency=self.currency)
|
||||||
|
|
||||||
if commit:
|
if commit:
|
||||||
self.save()
|
self.save()
|
||||||
|
@ -41,7 +41,13 @@ class TotalPriceMixin(serializers.Serializer):
|
|||||||
read_only=True,
|
read_only=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
total_price_currency = InvenTreeCurrencySerializer(read_only=True)
|
order_currency = InvenTreeCurrencySerializer(
|
||||||
|
allow_blank=True,
|
||||||
|
allow_null=True,
|
||||||
|
required=False,
|
||||||
|
label=_('Order Currency'),
|
||||||
|
help_text=_('Currency for this order (leave blank to use company default)'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AbstractOrderSerializer(serializers.Serializer):
|
class AbstractOrderSerializer(serializers.Serializer):
|
||||||
@ -168,7 +174,7 @@ class PurchaseOrderSerializer(TotalPriceMixin, AbstractOrderSerializer, InvenTre
|
|||||||
'supplier_detail',
|
'supplier_detail',
|
||||||
'supplier_reference',
|
'supplier_reference',
|
||||||
'total_price',
|
'total_price',
|
||||||
'total_price_currency',
|
'order_currency',
|
||||||
])
|
])
|
||||||
|
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
@ -178,7 +184,8 @@ class PurchaseOrderSerializer(TotalPriceMixin, AbstractOrderSerializer, InvenTre
|
|||||||
]
|
]
|
||||||
|
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'supplier': {'required': True}
|
'supplier': {'required': True},
|
||||||
|
'order_currency': {'required': False},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -707,7 +714,7 @@ class SalesOrderSerializer(TotalPriceMixin, AbstractOrderSerializer, InvenTreeMo
|
|||||||
'customer_reference',
|
'customer_reference',
|
||||||
'shipment_date',
|
'shipment_date',
|
||||||
'total_price',
|
'total_price',
|
||||||
'total_price_currency',
|
'order_currency',
|
||||||
])
|
])
|
||||||
|
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
@ -716,6 +723,10 @@ class SalesOrderSerializer(TotalPriceMixin, AbstractOrderSerializer, InvenTreeMo
|
|||||||
'shipment_date',
|
'shipment_date',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
extra_kwargs = {
|
||||||
|
'order_currency': {'required': False},
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialization routine for the serializer"""
|
"""Initialization routine for the serializer"""
|
||||||
customer_detail = kwargs.pop('customer_detail', False)
|
customer_detail = kwargs.pop('customer_detail', False)
|
||||||
|
@ -215,7 +215,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<td>{{ order.responsible }}</td>
|
<td>{{ order.responsible }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% include "currency_data.html" with instance=order %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-dollar-sign'></span></td>
|
<td><span class='fas fa-dollar-sign'></span></td>
|
||||||
<td>{% trans "Total cost" %}</td>
|
<td>{% trans "Total cost" %}</td>
|
||||||
@ -224,7 +224,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% if tp == None %}
|
{% if tp == None %}
|
||||||
<span class='badge bg-warning'>{% trans "Total cost could not be calculated" %}</span>
|
<span class='badge bg-warning'>{% trans "Total cost could not be calculated" %}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% render_currency tp currency=order.supplier.currency %}
|
{% render_currency tp currency=order.currency %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</td>
|
</td>
|
||||||
|
@ -180,9 +180,7 @@ $('#new-po-line').click(function() {
|
|||||||
createPurchaseOrderLineItem({{ order.pk }}, {
|
createPurchaseOrderLineItem({{ order.pk }}, {
|
||||||
{% if order.supplier %}
|
{% if order.supplier %}
|
||||||
supplier: {{ order.supplier.pk }},
|
supplier: {{ order.supplier.pk }},
|
||||||
{% if order.supplier.currency %}
|
currency: '{{ order.currency }}',
|
||||||
currency: '{{ order.supplier.currency }}',
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
onSuccess: function() {
|
onSuccess: function() {
|
||||||
$('#po-line-table').bootstrapTable('refresh');
|
$('#po-line-table').bootstrapTable('refresh');
|
||||||
@ -235,9 +233,7 @@ onPanelLoad('order-items', function() {
|
|||||||
order: {{ order.pk }},
|
order: {{ order.pk }},
|
||||||
table: '#po-extra-lines-table',
|
table: '#po-extra-lines-table',
|
||||||
url: '{% url "api-po-extra-line-list" %}',
|
url: '{% url "api-po-extra-line-list" %}',
|
||||||
{% if order.supplier.currency %}
|
currency: '{{ order.currency }}',
|
||||||
currency: '{{ order.supplier.currency }}',
|
|
||||||
{% endif %}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<td>{{ order.responsible }}</td>
|
<td>{{ order.responsible }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% include "currency_data.html" with instance=order %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-dollar-sign'></span></td>
|
<td><span class='fas fa-dollar-sign'></span></td>
|
||||||
<td>{% trans "Total Cost" %}</td>
|
<td>{% trans "Total Cost" %}</td>
|
||||||
@ -192,7 +192,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% if tp == None %}
|
{% if tp == None %}
|
||||||
<span class='badge bg-warning'>{% trans "Total cost could not be calculated" %}</span>
|
<span class='badge bg-warning'>{% trans "Total cost could not be calculated" %}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% render_currency tp currency=order.customer.currency %}
|
{% render_currency tp currency=order.currency %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</td>
|
</td>
|
||||||
|
@ -223,7 +223,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<td>{{ order.responsible }}</td>
|
<td>{{ order.responsible }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% include "currency_data.html" with instance=order %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-dollar-sign'></span></td>
|
<td><span class='fas fa-dollar-sign'></span></td>
|
||||||
<td>{% trans "Total Cost" %}</td>
|
<td>{% trans "Total Cost" %}</td>
|
||||||
@ -232,7 +232,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% if tp == None %}
|
{% if tp == None %}
|
||||||
<span class='badge bg-warning'>{% trans "Total cost could not be calculated" %}</span>
|
<span class='badge bg-warning'>{% trans "Total cost could not be calculated" %}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% render_currency tp currency=order.customer.currency %}
|
{% render_currency tp currency=order.currency %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</td>
|
</td>
|
||||||
|
@ -275,9 +275,7 @@
|
|||||||
order: {{ order.pk }},
|
order: {{ order.pk }},
|
||||||
table: '#so-extra-lines-table',
|
table: '#so-extra-lines-table',
|
||||||
url: '{% url "api-so-extra-line-list" %}',
|
url: '{% url "api-so-extra-line-list" %}',
|
||||||
{% if order.customer.currency %}
|
currency: '{{ order.currency }}',
|
||||||
currency: '{{ order.customer.currency }}',
|
|
||||||
{% endif %}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,6 +60,50 @@ class PurchaseOrderTest(OrderTest):
|
|||||||
|
|
||||||
LIST_URL = reverse('api-po-list')
|
LIST_URL = reverse('api-po-list')
|
||||||
|
|
||||||
|
def test_options(self):
|
||||||
|
"""Test the PurchaseOrder OPTIONS endpoint."""
|
||||||
|
|
||||||
|
self.assignRole('purchase_order.add')
|
||||||
|
|
||||||
|
response = self.options(self.LIST_URL, expected_code=200)
|
||||||
|
|
||||||
|
data = response.data
|
||||||
|
self.assertEqual(data['name'], 'Purchase Order List')
|
||||||
|
|
||||||
|
post = data['actions']['POST']
|
||||||
|
|
||||||
|
def check_options(data, field_name, spec):
|
||||||
|
"""Helper function to check that the options are configured correctly."""
|
||||||
|
field_data = data[field_name]
|
||||||
|
|
||||||
|
for k, v in spec.items():
|
||||||
|
self.assertIn(k, field_data)
|
||||||
|
self.assertEqual(field_data[k], v)
|
||||||
|
|
||||||
|
# Checks for the 'order_currency' field
|
||||||
|
check_options(post, 'order_currency', {
|
||||||
|
'type': 'choice',
|
||||||
|
'required': False,
|
||||||
|
'read_only': False,
|
||||||
|
'label': 'Order Currency',
|
||||||
|
'help_text': 'Currency for this order (leave blank to use company default)',
|
||||||
|
})
|
||||||
|
|
||||||
|
# Checks for the 'reference' field
|
||||||
|
check_options(post, 'reference', {
|
||||||
|
'type': 'string',
|
||||||
|
'required': True,
|
||||||
|
'read_only': False,
|
||||||
|
'label': 'Reference',
|
||||||
|
})
|
||||||
|
|
||||||
|
# Checks for the 'supplier' field
|
||||||
|
check_options(post, 'supplier', {
|
||||||
|
'type': 'related field',
|
||||||
|
'required': True,
|
||||||
|
'api_url': '/api/company/',
|
||||||
|
})
|
||||||
|
|
||||||
def test_po_list(self):
|
def test_po_list(self):
|
||||||
"""Test the PurchaseOrder list API endpoint"""
|
"""Test the PurchaseOrder list API endpoint"""
|
||||||
# List *ALL* PurchaseOrder items
|
# List *ALL* PurchaseOrder items
|
||||||
@ -155,7 +199,7 @@ class PurchaseOrderTest(OrderTest):
|
|||||||
|
|
||||||
for result in response.data['results']:
|
for result in response.data['results']:
|
||||||
self.assertIn('total_price', result)
|
self.assertIn('total_price', result)
|
||||||
self.assertIn('total_price_currency', result)
|
self.assertIn('order_currency', result)
|
||||||
|
|
||||||
def test_overdue(self):
|
def test_overdue(self):
|
||||||
"""Test "overdue" status."""
|
"""Test "overdue" status."""
|
||||||
@ -323,13 +367,18 @@ class PurchaseOrderTest(OrderTest):
|
|||||||
|
|
||||||
self.assertTrue(po.lines.count() > 0)
|
self.assertTrue(po.lines.count() > 0)
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
|
||||||
# Add some extra line items to this order
|
# Add some extra line items to this order
|
||||||
for idx in range(5):
|
for idx in range(5):
|
||||||
models.PurchaseOrderExtraLine.objects.create(
|
lines.append(models.PurchaseOrderExtraLine(
|
||||||
order=po,
|
order=po,
|
||||||
quantity=idx + 10,
|
quantity=idx + 10,
|
||||||
reference='some reference',
|
reference='some reference',
|
||||||
)
|
))
|
||||||
|
|
||||||
|
# bulk create orders
|
||||||
|
models.PurchaseOrderExtraLine.objects.bulk_create(lines)
|
||||||
|
|
||||||
data = self.get(reverse('api-po-detail', kwargs={'pk': 1})).data
|
data = self.get(reverse('api-po-detail', kwargs={'pk': 1})).data
|
||||||
|
|
||||||
@ -1157,7 +1206,7 @@ class SalesOrderTest(OrderTest):
|
|||||||
|
|
||||||
for result in response.data['results']:
|
for result in response.data['results']:
|
||||||
self.assertIn('total_price', result)
|
self.assertIn('total_price', result)
|
||||||
self.assertIn('total_price_currency', result)
|
self.assertIn('order_currency', result)
|
||||||
|
|
||||||
def test_overdue(self):
|
def test_overdue(self):
|
||||||
"""Test "overdue" status."""
|
"""Test "overdue" status."""
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<th>{% trans "Total" %}</th>
|
<th>{% trans "Total" %}</th>
|
||||||
<td>{% render_currency order.total_price decimal_places=2 currency=order.supplier.currency %}</td>
|
<td>{% render_currency order.total_price decimal_places=2 currency=order.currency %}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<th>{% trans "Total" %}</th>
|
<th>{% trans "Total" %}</th>
|
||||||
<td>{% render_currency order.total_price currency=order.customer.currency %}</td>
|
<td>{% render_currency order.total_price currency=order.currency %}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
12
InvenTree/templates/currency_data.html
Normal file
12
InvenTree/templates/currency_data.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% if instance and instance.currency %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-dollar-sign'></span></td>
|
||||||
|
<td>{% trans "Currency" %}</td>
|
||||||
|
{% if instance.order_currency %}
|
||||||
|
<td>{{ instance.currency }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td><em>{{ instance.currency }}</em></td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
@ -65,6 +65,9 @@ function purchaseOrderFields(options={}) {
|
|||||||
project_code: {
|
project_code: {
|
||||||
icon: 'fa-list',
|
icon: 'fa-list',
|
||||||
},
|
},
|
||||||
|
order_currency: {
|
||||||
|
icon: 'fa-coins',
|
||||||
|
},
|
||||||
target_date: {
|
target_date: {
|
||||||
icon: 'fa-calendar-alt',
|
icon: 'fa-calendar-alt',
|
||||||
},
|
},
|
||||||
@ -1670,7 +1673,7 @@ function loadPurchaseOrderTable(table, options) {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
return formatCurrency(value, {
|
return formatCurrency(value, {
|
||||||
currency: row.total_price_currency,
|
currency: row.order_currency,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -49,6 +49,9 @@ function returnOrderFields(options={}) {
|
|||||||
project_code: {
|
project_code: {
|
||||||
icon: 'fa-list',
|
icon: 'fa-list',
|
||||||
},
|
},
|
||||||
|
order_currency: {
|
||||||
|
icon: 'fa-coins',
|
||||||
|
},
|
||||||
target_date: {
|
target_date: {
|
||||||
icon: 'fa-calendar-alt',
|
icon: 'fa-calendar-alt',
|
||||||
},
|
},
|
||||||
@ -349,7 +352,7 @@ function loadReturnOrderTable(table, options={}) {
|
|||||||
visible: false,
|
visible: false,
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
return formatCurrency(value, {
|
return formatCurrency(value, {
|
||||||
currency: row.total_price_currency
|
currency: row.order_currency
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,9 @@ function salesOrderFields(options={}) {
|
|||||||
project_code: {
|
project_code: {
|
||||||
icon: 'fa-list',
|
icon: 'fa-list',
|
||||||
},
|
},
|
||||||
|
order_currency: {
|
||||||
|
icon: 'fa-coins',
|
||||||
|
},
|
||||||
target_date: {
|
target_date: {
|
||||||
icon: 'fa-calendar-alt',
|
icon: 'fa-calendar-alt',
|
||||||
},
|
},
|
||||||
@ -802,7 +805,7 @@ function loadSalesOrderTable(table, options) {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
return formatCurrency(value, {
|
return formatCurrency(value, {
|
||||||
currency: row.total_price_currency,
|
currency: row.order_currency,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ $ pip install -r requirements.txt
|
|||||||
To serve the pages locally, run the following command (from the top-level project directory):
|
To serve the pages locally, run the following command (from the top-level project directory):
|
||||||
|
|
||||||
```
|
```
|
||||||
$ mkdocs serve -a localhost:8080
|
$ mkdocs serve -f docs/mkdocs.yml -a localhost:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
## Edit Documentation Files
|
## Edit Documentation Files
|
||||||
|
@ -23,6 +23,10 @@ Each Purchase Order has a specific status code which indicates the current state
|
|||||||
| Complete | The purchase order has been completed, and is now closed |
|
| Complete | The purchase order has been completed, and is now closed |
|
||||||
| Cancelled | The purchase order was cancelled, and is now closed |
|
| Cancelled | The purchase order was cancelled, and is now closed |
|
||||||
|
|
||||||
|
### Purchase Order Currency
|
||||||
|
|
||||||
|
The currency code can be specified for an individual purchase order. If not specified, the default currency specified against the [supplier](./supplier.md) will be used.
|
||||||
|
|
||||||
## Create Purchase Order
|
## Create Purchase Order
|
||||||
|
|
||||||
Once the purchase order page is loaded, click on <span class="badge inventree add"><span class='fas fa-plus-circle'></span> New Purchase Order</span> which opens the "Create Purchase Order" form.
|
Once the purchase order page is loaded, click on <span class="badge inventree add"><span class='fas fa-plus-circle'></span> New Purchase Order</span> which opens the "Create Purchase Order" form.
|
||||||
|
@ -27,9 +27,11 @@ In addition to the default report context variables, the following variables are
|
|||||||
| order.creation_date | The date when the order was created |
|
| order.creation_date | The date when the order was created |
|
||||||
| order.target_date | The date when the order should arrive |
|
| order.target_date | The date when the order should arrive |
|
||||||
| order.if_overdue | Boolean value that tells if the target date has passed |
|
| order.if_overdue | Boolean value that tells if the target date has passed |
|
||||||
|
| order.currency | The currency code associated with this order, e.g. 'AUD' |
|
||||||
|
|
||||||
#### Lines
|
#### Lines
|
||||||
The lines have sub variables.
|
|
||||||
|
Each line item have sub variables, as follows:
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@ -39,9 +41,20 @@ The lines have sub variables.
|
|||||||
| reference | The reference given in the part of the order |
|
| reference | The reference given in the part of the order |
|
||||||
| notes | The notes given in the part of the order |
|
| notes | The notes given in the part of the order |
|
||||||
| target_date | The date when the part should arrive. Each part can have an individual date |
|
| target_date | The date when the part should arrive. Each part can have an individual date |
|
||||||
| price | The price the part supplierpart |
|
| price | The unit price the line item |
|
||||||
|
| total_line_price | The total price for this line item, calculated from the unit price and quantity |
|
||||||
| destination | The stock location where the part will be stored |
|
| destination | The stock location where the part will be stored |
|
||||||
|
|
||||||
|
A simple example below shows how to use the context variables for line items:
|
||||||
|
|
||||||
|
```html
|
||||||
|
{% raw %}
|
||||||
|
{% for line in order.lines %}
|
||||||
|
Price: {% render_currency line.total_line_price %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endraw %}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Default Report Template
|
### Default Report Template
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ In addition to the default report context variables, the following variables are
|
|||||||
| customer | The [customer](../sell/customer.md) associated with the particular sales order |
|
| customer | The [customer](../sell/customer.md) associated with the particular sales order |
|
||||||
| lines | A list of available line items for this order |
|
| lines | A list of available line items for this order |
|
||||||
| extra_lines | A list of available *extra* line items for this order |
|
| extra_lines | A list of available *extra* line items for this order |
|
||||||
|
| order.currency | The currency code associated with this order, e.g. 'CAD' |
|
||||||
|
|
||||||
### Default Report Template
|
### Default Report Template
|
||||||
|
|
||||||
|
@ -23,6 +23,11 @@ Each Sales Order has a specific status code, which represents the state of the o
|
|||||||
| Complete | The sales order has been completed, and is now closed |
|
| Complete | The sales order has been completed, and is now closed |
|
||||||
| Cancelled | The sales order was cancelled, and is now closed |
|
| Cancelled | The sales order was cancelled, and is now closed |
|
||||||
|
|
||||||
|
### Sales Order Currency
|
||||||
|
|
||||||
|
The currency code can be specified for an individual sales order. If not specified, the default currency specified against the [customer](./customer.md) will be used.
|
||||||
|
|
||||||
|
|
||||||
## Create a Sales Order
|
## Create a Sales Order
|
||||||
|
|
||||||
Once the sales order page is loaded, click on <span class="badge inventree add"><span class='fas fa-plus-circle'></span> New Sales Order</span> which opens the "Create Sales Order" form.
|
Once the sales order page is loaded, click on <span class="badge inventree add"><span class='fas fa-plus-circle'></span> New Sales Order</span> which opens the "Create Sales Order" form.
|
||||||
|
Loading…
Reference in New Issue
Block a user