[ADD] Inventory: add stock valuation cheat sheet
An adaptation of the venerable business memento from 8.0 to the new inventory valuation mechanics of Odoo 19.0 by scavenging and cobbling together of the scripts `entries.js` and `coa-valuation.js`. The shared data is kept in a separate file. Additionally, we remove the old inventory valuation documentation. Task ID: 5107300 closes odoo/documentation#14906 Signed-off-by: Felicia Kuan (feku) <feku@odoo.com>
@@ -282,7 +282,7 @@ available methods are standard price, average price, :abbr:`LIFO (Last-In, First
|
||||
:abbr:`FIFO (First-In, First-Out).`
|
||||
|
||||
.. seealso::
|
||||
:doc:`../inventory_and_mrp/inventory/product_management/inventory_valuation/inventory_valuation_config`
|
||||
:doc:`../inventory_and_mrp/inventory/inventory_valuation/cheat_sheet`
|
||||
|
||||
.. _accounting/retained-earnings:
|
||||
|
||||
|
||||
@@ -25,14 +25,8 @@ valuation <inventory/avg_price/leaving_inventory>`.
|
||||
|
||||
.. note::
|
||||
This document addresses a specific use case for theoretical purposes. For instructions on how to
|
||||
set up and use |AVCO|, refer to the :doc:`inventory valuation configuration
|
||||
<../../../inventory_and_mrp/inventory/product_management/inventory_valuation/inventory_valuation_config>`
|
||||
doc.
|
||||
|
||||
.. seealso::
|
||||
- :doc:`Using inventory valuation
|
||||
<../../../inventory_and_mrp/inventory/product_management/inventory_valuation/using_inventory_valuation>`
|
||||
- :ref:`Other inventory valuation methods <inventory/warehouses_storage/costing_methods>`
|
||||
set up and use |AVCO|, refer to the :doc:`inventory valuation cheat sheet
|
||||
<../../../inventory_and_mrp/inventory/inventory_valuation/cheat_sheet>`.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
@@ -43,8 +37,8 @@ product category page, set :guilabel:`Costing Method` to `Average Cost (AVCO)` a
|
||||
:guilabel:`Inventory Valuation` to `Automated`.
|
||||
|
||||
.. seealso::
|
||||
:doc:`Inventory valuation configuration
|
||||
<../../../inventory_and_mrp/inventory/product_management/inventory_valuation/inventory_valuation_config>`
|
||||
:doc:`Inventory valuation cheat sheet
|
||||
<../../../inventory_and_mrp/inventory/inventory_valuation/cheat_sheet>`
|
||||
|
||||
Using average cost valuation
|
||||
============================
|
||||
@@ -70,7 +64,7 @@ When new products arrive, the new average cost for each product is recomputed us
|
||||
- **Purchase Price**: estimated price of products at the reception of products (since vendor bills
|
||||
may arrive later). The amount includes not only the price for the products, but also added costs,
|
||||
such as shipping, taxes, and :doc:`landed costs
|
||||
<../../../inventory_and_mrp/inventory/product_management/inventory_valuation/landed_costs>`. At
|
||||
<../../../inventory_and_mrp/inventory/inventory_valuation/landed_costs>`. At
|
||||
reception of the vendor bill, this price is adjusted;
|
||||
- **Final Qty**: quantity of on-hand stock after the stock move.
|
||||
|
||||
@@ -263,9 +257,6 @@ account that tracks the amount to be paid to vendors. Once a vendor delivers an
|
||||
value** increases based on the vendor price of the products that have entered the stock. The holding
|
||||
account (called **stock input**) is credited and only reconciled once the vendor bill is received.
|
||||
|
||||
.. seealso::
|
||||
- :ref:`Anglo-Saxon vs. Continental <inventory/warehouses_storage/accounting-types>`
|
||||
|
||||
The table below reflects journal entries and accounts. The *stock input* account stores the money
|
||||
intended to pay vendors when the vendor bill has not yet been received. To balance accounts when
|
||||
returning products that have a price difference between the price the product is **valued at** and
|
||||
|
||||
@@ -1553,7 +1553,7 @@ Configuration
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
In order to track the correct customs number for a specific invoice, Odoo uses :doc:`landed costs
|
||||
<../../inventory_and_mrp/inventory/product_management/inventory_valuation/landed_costs>`. Go to
|
||||
<../../inventory_and_mrp/inventory/inventory_valuation/landed_costs>`. Go to
|
||||
:menuselection:`Inventory --> Configuration --> Settings`, and in the :guilabel:`Valuation` section,
|
||||
make sure that :guilabel:`Landed Costs` is activated.
|
||||
|
||||
@@ -1569,8 +1569,7 @@ and complete these three requirements:
|
||||
Number` but **not** :guilabel:`By Quantity`.
|
||||
- :guilabel:`Invoicing Policy` **must** be set to :guilabel:`Delivered quantities`.
|
||||
- :doc:`Valuation by lots/serial numbers
|
||||
<../../inventory_and_mrp/inventory/product_management/inventory_valuation/valuation_by_lots>`
|
||||
**must** be enabled.
|
||||
<../../inventory_and_mrp/inventory/inventory_valuation/valuation_by_lots>` **must** be enabled.
|
||||
|
||||
This will make the field :guilabel:`Customs invoicing` available on the :guilabel:`Accounting` tab.
|
||||
Enable the field to use customs numbers with this product.
|
||||
@@ -1583,8 +1582,8 @@ configuration:
|
||||
|
||||
.. note::
|
||||
The feature works regardless of whether the :doc:`inventory valuation
|
||||
<../../inventory_and_mrp/inventory/product_management/inventory_valuation/using_inventory_valuation>`
|
||||
is set to either :guilabel:`Periodic (at closing)` or :guilabel:`Perpetual (at invoicing)`.
|
||||
<../../inventory_and_mrp/inventory/inventory_valuation/cheat_sheet>` is set to either
|
||||
:guilabel:`Periodic (at closing)` or :guilabel:`Perpetual (at invoicing)`.
|
||||
|
||||
.. image:: mexico/mx-landing-configuration.png
|
||||
:alt: Storable products general configuration.
|
||||
@@ -1610,7 +1609,7 @@ number`.
|
||||
While it is possible to add costs related to the customs number at this stage of the process, it
|
||||
is highly recommended to create a landed cost from a vendor bill from your customs agent. Learn
|
||||
more about :doc:`Landed Costs here
|
||||
<../../inventory_and_mrp/inventory/product_management/inventory_valuation/landed_costs>`.
|
||||
<../../inventory_and_mrp/inventory/inventory_valuation/landed_costs>`.
|
||||
|
||||
.. warning::
|
||||
The :guilabel:`Customs number` field is not editable once it is set, and cannot be repeated,
|
||||
|
||||
@@ -1003,12 +1003,11 @@ Several configurations related to the product or product category are necessary
|
||||
|
||||
- **Automatic inventory valuation**: For storable goods (:dfn:`products with tracked inventory`),
|
||||
use :doc:`automatic inventory valuation
|
||||
<../../inventory_and_mrp/inventory/product_management/inventory_valuation/inventory_valuation_config>`.
|
||||
Once automatic inventory valuation is enabled, this valuation method can be enabled for
|
||||
a product's :ref:`product category <inventory/warehouses_storage/valuation-on-product-category>`.
|
||||
<../../inventory_and_mrp/inventory/inventory_valuation/cheat_sheet>`. Once automatic inventory
|
||||
valuation is enabled, this valuation method can be enabled for a product's product category.
|
||||
|
||||
- **Costing method:** Storable goods must use a :doc:`costing method
|
||||
<../../inventory_and_mrp/inventory/product_management/inventory_valuation/inventory_valuation_config>`
|
||||
<../../inventory_and_mrp/inventory/inventory_valuation/cheat_sheet>`
|
||||
**other** than :guilabel:`Standard Price`, as the journal entries generated from stock moves are
|
||||
used to populate the |PLE| reports.
|
||||
|
||||
@@ -1041,7 +1040,7 @@ Generate a .txt file for permanent inventory Kardex reports
|
||||
|PLE| 12.1 and 13.1 come as two separate books. The books need to be downloaded in `.txt` file
|
||||
format from Odoo, and then they should be submitted to the |SUNAT| |PLE| software.
|
||||
|
||||
On the :ref:`Inventory Valuation Report <inventory/management/reporting/valuation-report>`, click
|
||||
On the :ref:`Inventory Valuation Report <inventory/product_management/valuation-report>`, click
|
||||
the :guilabel:`PLE Reports` button. Then, select the :guilabel:`Period` and choose a report to
|
||||
export: either the :guilabel:`PLE 12.1` or :guilabel:`PLE 13.1`. Odoo generates a `.txt` file
|
||||
for the chosen report.
|
||||
|
||||
@@ -19,3 +19,4 @@ users to easily manage lead times, automate replenishment, configure advanced ro
|
||||
inventory/product_management
|
||||
inventory/warehouses_storage
|
||||
inventory/shipping_receiving
|
||||
inventory/inventory_valuation
|
||||
|
||||
@@ -7,8 +7,7 @@ Inventory valuation
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
inventory_valuation/inventory_valuation_config
|
||||
inventory_valuation/using_inventory_valuation
|
||||
inventory_valuation/cheat_sheet
|
||||
inventory_valuation/landed_costs
|
||||
inventory_valuation/valuation_by_lots
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
:code-column:
|
||||
:custom-css: valuation.css
|
||||
:custom-js: misc.js,valuation-data.js,valuation-journal.js,valuation-accounting.js
|
||||
|
||||
=====================
|
||||
Valuation cheat sheet
|
||||
=====================
|
||||
|
||||
.. rst-class:: full-width
|
||||
|
||||
|
||||
Costing Methods
|
||||
===============
|
||||
|
||||
Odoo supports 3 costing methods configured in accounting's settings and, optionally,
|
||||
the product's category.
|
||||
|
||||
.. rst-class:: alternatives doc-aside
|
||||
|
||||
Standard Cost: fixed unit cost, updated manually
|
||||
.. list-table::
|
||||
:widths: 28 18 18 18 18
|
||||
:header-rows: 1
|
||||
:stub-columns: 1
|
||||
:class: values-table
|
||||
|
||||
* - Operation
|
||||
- Unit Cost
|
||||
- Qty On Hand
|
||||
- Delta Value
|
||||
- Inventory Value
|
||||
* -
|
||||
- $10
|
||||
- 0
|
||||
-
|
||||
- $0
|
||||
* - Receive 8 @$10
|
||||
- $10
|
||||
- 8
|
||||
- +8×$10
|
||||
- $80
|
||||
* - Receive 4 @$16
|
||||
- $10
|
||||
- 12
|
||||
- +4×$10
|
||||
- $120
|
||||
* - Deliver 10
|
||||
- $10
|
||||
- 2
|
||||
- | -10×$10
|
||||
|
|
||||
- $20
|
||||
* - Receive 2 @$9
|
||||
- $10
|
||||
- 4
|
||||
- +2×$10
|
||||
- $40
|
||||
|
||||
Average Cost: weighted average of all units
|
||||
.. list-table::
|
||||
:widths: 28 18 18 18 18
|
||||
:header-rows: 1
|
||||
:stub-columns: 1
|
||||
:class: values-table
|
||||
|
||||
* - Operation
|
||||
- Unit Cost
|
||||
- Qty On Hand
|
||||
- Delta Value
|
||||
- Inventory Value
|
||||
* -
|
||||
- $0
|
||||
- 0
|
||||
-
|
||||
- $0
|
||||
* - Receive 8 @$10
|
||||
- $10
|
||||
- 8
|
||||
- +8×$10
|
||||
- $80
|
||||
* - Receive 4 @$16
|
||||
- $12
|
||||
- 12
|
||||
- +4×$16
|
||||
- $144
|
||||
* - Deliver 10
|
||||
- $12
|
||||
- 2
|
||||
- | -10×$12
|
||||
|
|
||||
- $24
|
||||
* - Receive 2 @$6
|
||||
- $9
|
||||
- 4
|
||||
- +2×$6
|
||||
- $36
|
||||
|
||||
FIFO: first in, first out
|
||||
.. list-table::
|
||||
:widths: 28 18 18 18 18
|
||||
:header-rows: 1
|
||||
:stub-columns: 1
|
||||
:class: values-table
|
||||
|
||||
* - Operation
|
||||
- Unit Cost
|
||||
- Qty On Hand
|
||||
- Delta Value
|
||||
- Inventory Value
|
||||
* -
|
||||
- $0
|
||||
- 0
|
||||
-
|
||||
- $0
|
||||
* - Receive 8 @$10
|
||||
- $10
|
||||
- 8
|
||||
- +8×$10
|
||||
- $80
|
||||
* - Receive 4 @$16
|
||||
- $12
|
||||
- 12
|
||||
- +4×$16
|
||||
- $144
|
||||
* - Deliver 10
|
||||
- $16
|
||||
- 2
|
||||
- | -8×$10
|
||||
| -2×$16
|
||||
- $32
|
||||
* - Receive 2 @$6
|
||||
- $11
|
||||
- 4
|
||||
- +2×$6
|
||||
- $44
|
||||
|
||||
|
||||
.. rst-class:: alternatives-note
|
||||
|
||||
.. note:: Removal strategies also support :abbr:`LIFO (last in, first out)` and :abbr:`FEFO
|
||||
(first expiry, first out)`, but they only impact which product is first picked, not the
|
||||
valuation method. For example, you can pick using LIFO, but using average cost for valuation,
|
||||
as LIFO is not allowed by :abbr:`IFRS (International Financial Reporting Standards)`.
|
||||
|
||||
|
||||
Inventory vs Accounting
|
||||
=======================
|
||||
|
||||
.. rst-class:: inventory-app-paragraph
|
||||
|
||||
The :doc:`Inventory app </applications/inventory_and_mrp/inventory>` keeps track of the inventory
|
||||
value in real time as you **receive and deliver goods**. The reporting menu lets you analyze
|
||||
inventory quantities and values by company, location, product, and more.
|
||||
|
||||
.. rst-class:: accounting-app-paragraph
|
||||
|
||||
The :doc:`Accounting app </applications/finance/accounting>` updates accounts when you receive
|
||||
**invoices or bills**. Even though receipts and invoices differ, it’s not practical for
|
||||
accountants to post journal entries for every inventory movement. So, they post a closing entry
|
||||
to account for the difference between what has been invoiced and received/delivered. This closing
|
||||
process happens usually once a year for SMEs, or once a month for larger companies.
|
||||
|
||||
.. role:: good
|
||||
.. role:: meh
|
||||
.. role:: bad
|
||||
|
||||
.. h:div:: feature-table doc-aside
|
||||
|
||||
+------------------+------------+-----------+
|
||||
| | Accounting | Inventory |
|
||||
+==================+============+===========+
|
||||
| Purchase Order | :meh:`/` | :meh:`/` |
|
||||
+------------------+------------+-----------+
|
||||
| Receipt | :meh:`/` | :good:`✓` |
|
||||
+------------------+------------+-----------+
|
||||
| Vendor Bill | :good:`✓` | :meh:`/` |
|
||||
+------------------+------------+-----------+
|
||||
| Sales Order | :meh:`/` | :meh:`/` |
|
||||
+------------------+------------+-----------+
|
||||
| Customer Invoice | :good:`✓` | :meh:`/` |
|
||||
+------------------+------------+-----------+
|
||||
| Delivery | :meh:`/` | :good:`✓` |
|
||||
+------------------+------------+-----------+
|
||||
| Closing Entry | :good:`✓` | :meh:`/` |
|
||||
+------------------+------------+-----------+
|
||||
|
||||
|
||||
Accounting Methods
|
||||
==================
|
||||
|
||||
There are two accounting practices on how to maintain your accounts:
|
||||
|
||||
**Periodic:** Post vendor bills as expenses by nature, and update stock valuation in the closing
|
||||
entry by reducing expenses (stock variation). This is the best practice in Europe.
|
||||
|
||||
**Perpetual:** Post vendor bills as assets (stock valuation), report expenses when goods are sold
|
||||
(cost of goods sold). This is the best practice in countries that follow Anglo-Saxon accounting,
|
||||
like the USA and India.
|
||||
|
||||
.. role:: yellow
|
||||
.. role:: green
|
||||
.. role:: blue
|
||||
.. role:: darkblue
|
||||
.. role:: purple
|
||||
.. role:: washed
|
||||
.. role:: washed-green
|
||||
:class: washed green
|
||||
.. role:: washed-darkblue
|
||||
:class: washed darkblue
|
||||
.. role:: washed-purple
|
||||
:class: washed purple
|
||||
|
||||
* :purple:`Stock Account` on the product's category
|
||||
* :yellow:`Stock Variation` on the stock account
|
||||
* :blue:`Expense/Cost of Goods Sold` on the product/category
|
||||
* :green:`Inventory Adjustment` on the Inventory Loss location
|
||||
(optional, recommended for Anglo-Saxon accounting)
|
||||
* :darkblue:`Expense` on the stock account
|
||||
(for perpetual Continental accounting only)
|
||||
|
||||
.. h:div:: doc-aside
|
||||
|
||||
.. list-table::
|
||||
:stub-columns: 1
|
||||
:header-rows: 1
|
||||
:class: config-table
|
||||
|
||||
* -
|
||||
- EU Periodic
|
||||
- EU Perpetual
|
||||
- US Periodic
|
||||
- US Perpetual
|
||||
* - ADJUSTMENT
|
||||
-
|
||||
- :purple:`Stock`
|
||||
-
|
||||
- :purple:`Stock`
|
||||
* -
|
||||
-
|
||||
- :green:`LOSS`
|
||||
-
|
||||
- :green:`Shrinkage`
|
||||
* -
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
* - BILL
|
||||
- :blue:`Expense`
|
||||
- :purple:`Stock`
|
||||
- :blue:`COGS`
|
||||
- :purple:`Stock`
|
||||
* -
|
||||
- :washed:`Payable`
|
||||
- :washed:`Payable`
|
||||
- :washed:`Payable`
|
||||
- :washed:`Payable`
|
||||
* -
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
* - INVOICE
|
||||
-
|
||||
- :blue:`Expense`
|
||||
-
|
||||
- :blue:`COGS`
|
||||
* -
|
||||
-
|
||||
- :purple:`Stock`
|
||||
-
|
||||
- :purple:`Stock`
|
||||
* -
|
||||
- :washed:`Income`
|
||||
- :washed:`Income`
|
||||
- :washed:`Income`
|
||||
- :washed:`Income`
|
||||
* -
|
||||
- :washed:`Receivable`
|
||||
- :washed:`Receivable`
|
||||
- :washed:`Receivable`
|
||||
- :washed:`Receivable`
|
||||
* -
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
* - Closing
|
||||
- :purple:`Stock`
|
||||
- :washed-purple:`Stock`
|
||||
- :purple:`Stock`
|
||||
- :washed-purple:`Stock`
|
||||
* - [1]
|
||||
- :yellow:`Variation`
|
||||
- :washed-darkblue:`Expense`
|
||||
- :yellow:`Variation`
|
||||
- :yellow:`Variation`
|
||||
* - [2]
|
||||
- :washed-green:`LOSS`
|
||||
-
|
||||
- :washed-green:`Shrinkage`
|
||||
-
|
||||
* - [3]
|
||||
-
|
||||
- :yellow:`Variation`
|
||||
-
|
||||
-
|
||||
* -
|
||||
-
|
||||
- :darkblue:`Expense`
|
||||
-
|
||||
-
|
||||
|
||||
1. Inventory valuation - Accounting valuation
|
||||
2. Inventory valuation lost,
|
||||
only if an account is set on the loss location
|
||||
3. Accounting valuation end of period -
|
||||
Valuation beginning of period
|
||||
|
||||
|
||||
.. _accounting-entries:
|
||||
|
||||
Accounting Entries
|
||||
==================
|
||||
|
||||
.. h:div:: accounting-entries doc-aside
|
||||
|
||||
.. placeholder
|
||||
|
||||
|
||||
.. _journal-entries:
|
||||
|
||||
Journal Entries Configuration
|
||||
=============================
|
||||
|
||||
|
||||
.. h:div:: journal-entries doc-aside
|
||||
|
||||
.. placeholder
|
||||
|
||||
|
||||
Reporting
|
||||
=========
|
||||
|
||||
In Inventory
|
||||
------------
|
||||
|
||||
Open :menuselection:`Inventory -- > Reporting --> Stock` to view your current inventory level and
|
||||
valuation for each product, or to review historical data as of a previous date.
|
||||
|
||||
.. h:div:: doc-aside
|
||||
|
||||
.. image:: cheat_sheet/valuation-stock.png
|
||||
|
||||
|
||||
Unit cost
|
||||
~~~~~~~~~
|
||||
|
||||
To check a product's existing unit price updates and their origins, click on the product's
|
||||
:guilabel:`Unit Cost`. In :abbr:`AVCO (average cost)` this allows you to understand how the
|
||||
currently used value was calculated.
|
||||
|
||||
.. h:div:: doc-aside
|
||||
|
||||
.. image:: cheat_sheet/unit-cost.png
|
||||
|
||||
|
||||
Total value
|
||||
~~~~~~~~~~~
|
||||
|
||||
To see all incoming quantities for which you still have a remaining quantity and the value used for
|
||||
their valuation, click on a product's :guilabel:`Total Value`.
|
||||
|
||||
- In AVCO or standard cost, the used value is always the current average unit cost.
|
||||
- In FIFO, remaining units from each previous incoming move retain their own individual valuation.
|
||||
|
||||
In FIFO or AVCO, remaining quantities from a previous incoming move can have their value adjusted if
|
||||
necessary: Select the incoming moves to be adjusted, click :icon:`fa-cog` :guilabel:`Actions`, and
|
||||
then click :guilabel:`Adjust Valuation`. Enter the new :guilabel:`Value` and, optionally, a
|
||||
:guilabel:`Description`.
|
||||
|
||||
.. h:div:: doc-aside
|
||||
|
||||
.. image:: cheat_sheet/total-value.png
|
||||
|
||||
|
||||
In Accounting
|
||||
-------------
|
||||
|
||||
To view the difference between the accounting stock value and the current inventory value recorded
|
||||
thanks to the incoming moves with a remaining quantity, go to :menuselection:`Accounting --> Review
|
||||
--> Inventory Valuation`.
|
||||
|
||||
To generate a new accounting entry to review and post, click :guilabel:`Generate Entry`.
|
||||
|
||||
To view a list of sales and purchase orders for which accrual entries should be encoded, go to
|
||||
:menuselection:`Accounting --> Review` and select the relevant menu item (:guilabel:`Invoices not
|
||||
received`, :guilabel:`Invoices to be issued`, :guilabel:`Prepaid expenses`, or :guilabel:`Deferred
|
||||
Revenues`).
|
||||
|
||||
With Anglo-Saxon perpetual accounting, this will also help to distribute recorded inventory
|
||||
variations to accounts such as Bills to Receive/:abbr:`GRNI (goods received not invoiced)` or
|
||||
:abbr:`COGS (cost of goods sold)` as shown in the :ref:`Accounting Entries <accounting-entries>`
|
||||
and :ref:`Journal Entries Configuration <journal-entries>` sections.
|
||||
|
||||
.. h:div:: doc-aside
|
||||
|
||||
.. image:: cheat_sheet/valuation-accounting.png
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -69,9 +69,7 @@ When creating new vendor bills, this product can be added as an invoice line as
|
||||
|
||||
.. important::
|
||||
To apply a landed cost on a vendor bill, products in the original |PO| **must** belong to a
|
||||
*Product Category* with a *Costing Method* of either |AVCO| or |FIFO|, and the valuation method
|
||||
can be :doc:`manual <using_inventory_valuation>` or :doc:`automatic
|
||||
<inventory_valuation_config>`.
|
||||
*Product Category* with a *Costing Method* of either |AVCO| or |FIFO|.
|
||||
|
||||
Create purchase order
|
||||
=====================
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
@@ -2,19 +2,19 @@
|
||||
Valuation by lots/serial numbers
|
||||
================================
|
||||
|
||||
Track :doc:`inventory valuation <using_inventory_valuation>` by :doc:`lots or serial numbers
|
||||
<../../product_management/product_tracking>` to:
|
||||
Track :doc:`inventory valuation <cheat_sheet>` by :doc:`lots or serial numbers
|
||||
<../product_management/product_tracking>` to:
|
||||
|
||||
#. :ref:`Compare and differentiate purchasing cost <inventory/product_management/view-valuation>`,
|
||||
based on lot or serial numbers.
|
||||
#. Track the actual cost of manufactured products, based on the real cost of each tracked component
|
||||
used.
|
||||
#. Depreciate specific lot or serial numbers when they :doc:`sit in stock for too long
|
||||
<../../warehouses_storage/reporting/aging>`.
|
||||
<../warehouses_storage/reporting/aging>`.
|
||||
|
||||
.. important::
|
||||
Please read this :doc:`introduction to inventory valuation <inventory_valuation_config>` before
|
||||
setting up valuation by lot/serial numbers.
|
||||
Please read this :doc:`introduction to inventory valuation <cheat_sheet>` before setting up
|
||||
valuation by lot/serial numbers.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
@@ -25,16 +25,13 @@ feature <inventory/product_management/enable-lot-serial>`. After that, go to
|
||||
a new product, by clicking :guilabel:`New`.
|
||||
|
||||
On the product form, in the :guilabel:`Category` field, choose a product category. Ensure the
|
||||
product category's :ref:`Costing Method <inventory/warehouses_storage/costing_methods>` is set to
|
||||
*First In First Out (FIFO)* or *Average Cost (AVCO)*.
|
||||
product category's :guilabel:`Costing Method` is set to *First In First Out (FIFO)* or
|
||||
*Average Cost (AVCO)*.
|
||||
|
||||
.. tip::
|
||||
To check the costing method set on the product category, hover over the :guilabel:`Category`
|
||||
field, and click the :icon:`oi-arrow-right` :guilabel:`(Internal Link)` icon.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Costing methods <inventory/warehouses_storage/costing_methods>`
|
||||
|
||||
Next, activate the product to be tracked by lots or serial numbers by ticking the :guilabel:`Track
|
||||
Inventory` checkbox. Then, click the adjacent field that appears, and choose either :guilabel:`By
|
||||
Lots` or :guilabel:`By Unique Serial Number` from the resulting drop-down menu.
|
||||
@@ -109,7 +106,7 @@ Create new lot/serial number
|
||||
----------------------------
|
||||
|
||||
Creating a new lot/serial number through an :doc:`inventory adjustment
|
||||
<../../warehouses_storage/inventory_management/count_products>` assigns the same value as the cost
|
||||
<../warehouses_storage/inventory_management/count_products>` assigns the same value as the cost
|
||||
on the product form.
|
||||
|
||||
To make an inventory adjustment, and assign a lot number, go to :menuselection:`Inventory app -->
|
||||
@@ -187,7 +184,7 @@ On the resulting :guilabel:`Stock Valuation` report, click the search bar, and i
|
||||
|
||||
.. tip::
|
||||
Click the :icon:`fa-plus` :guilabel:`(plus)` icon to the right of a collapsed lot number line to
|
||||
:ref:`manually modify the cost <inventory/product_management/update-unit-price>`.
|
||||
manually modify the cost.
|
||||
|
||||
This is useful for adjusting individual lot prices when a purchase order or bill includes
|
||||
multiple lots/serial numbers, as initial prices are identical upon reception.
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
@@ -9,4 +9,3 @@ Product management
|
||||
|
||||
product_management/configure
|
||||
product_management/product_tracking
|
||||
product_management/inventory_valuation
|
||||
|
||||
@@ -164,7 +164,7 @@ documents.
|
||||
* - :doc:`Use inventory adjustments <../../warehouses_storage/inventory_management/count_products>`
|
||||
- Yes
|
||||
- No
|
||||
* - :doc:`Use inventory valuation <../inventory_valuation/using_inventory_valuation>`
|
||||
* - :doc:`Use inventory valuation <../../inventory_valuation/cheat_sheet>`
|
||||
- Yes
|
||||
- No
|
||||
* - :ref:`Create transfer <inventory/product_management/transfer-store>`
|
||||
@@ -344,6 +344,6 @@ Inventory reports
|
||||
on that product's specific moves history.
|
||||
- :guilabel:`Moves Analysis`: This report provides a pivot table view of inventory transfers by
|
||||
operation type.
|
||||
- :ref:`Stock Valuation report <inventory/management/reporting/valuation-report>`: A detailed record
|
||||
of the monetary value of all tracked inventory.
|
||||
- :ref:`Stock Valuation report <inventory/product_management/valuation-report>`: A
|
||||
detailed record of the monetary value of all tracked inventory.
|
||||
|
||||
|
||||
@@ -1,488 +0,0 @@
|
||||
=============================
|
||||
Automatic inventory valuation
|
||||
=============================
|
||||
|
||||
.. |right arrow| replace:: :icon:`fa-arrow-right` :guilabel:`(right arrow)`
|
||||
|
||||
All of a company's stock on-hand contributes to the valuation of its inventory. That value should
|
||||
be reflected in the company's accounting records to accurately show the value of the company and
|
||||
all of its assets.
|
||||
|
||||
By default, Odoo uses a periodic inventory valuation (also known as manual inventory valuation).
|
||||
This method implies that the accounting team manually posts journal entries, based on the physical
|
||||
inventory of the company, and warehouse employees take the time to count the stock. In Odoo, each
|
||||
product category reflects this, with the :guilabel:`Costing Method` set to :guilabel:`Standard
|
||||
Price`, and the :guilabel:`Inventory Valuation` (not visible by default) set to :guilabel:`Manual`.
|
||||
|
||||
.. image:: inventory_valuation_config/inventory-valuation-fields.png
|
||||
:align: center
|
||||
:alt: The Costing Method field is located on the Product Categories form.
|
||||
|
||||
Alternatively, perpetual (automatic) inventory valuation creates real-time *journal entries* in the
|
||||
*Accounting* app whenever stock enters or leaves the company's warehouse.
|
||||
|
||||
This document is focused on the proper setup of automatic inventory valuation, which is an
|
||||
integrated valuation method that ensures journal entries in the *Accounting* app match stock
|
||||
valuation updates in the *Inventory* app. For an introduction of inventory valuation in Odoo, refer
|
||||
to the :doc:`using_inventory_valuation` documentation.
|
||||
|
||||
.. warning::
|
||||
Switching from manual to automatic inventory valuation may cause discrepancies between stock
|
||||
valuation and accounting journals.
|
||||
|
||||
One `successful strategy <https://www.odoo.com/r/Kvfg>`_ for switching to automated valuation:
|
||||
|
||||
#. Clear existing stock (possibly with an :doc:`inventory adjustment
|
||||
<../../warehouses_storage/inventory_management/count_products>`)
|
||||
#. Change the inventory valuation method to *Automatic*
|
||||
#. Return the existing stock, with the original monetary value (using an inventory adjustment)
|
||||
|
||||
Once the existing stock is recovered, the Odoo *Accounting* app automatically generates the
|
||||
journal entries to corresponding stock valuation records.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
To properly set up automatic inventory valuation, follow these steps in Odoo:
|
||||
|
||||
#. :ref:`Install Accounting app and enable specific settings
|
||||
<inventory/warehouses_storage/accounting-setup>`
|
||||
#. :ref:`Set Automatic inventory valuation on product categories
|
||||
<inventory/warehouses_storage/valuation-on-product-category>`
|
||||
#. :ref:`Set costing method <inventory/warehouses_storage/costing_methods>`
|
||||
|
||||
.. _inventory/warehouses_storage/accounting-setup:
|
||||
|
||||
Accounting setup
|
||||
----------------
|
||||
|
||||
To use automatic inventory valuation, install the *Accounting* app. Next, go to
|
||||
:menuselection:`Accounting app --> Configuration --> Settings`, and in the :guilabel:`Stock
|
||||
Valuation` section, tick the :guilabel:`Automatic Accounting` checkbox. Then, click
|
||||
:guilabel:`Save`.
|
||||
|
||||
.. note::
|
||||
Enabling :guilabel:`Automatic Accounting` shows the previously invisible *Inventory Valuation*
|
||||
field on a product category.
|
||||
|
||||
.. image:: inventory_valuation_config/auto-accounting.png
|
||||
:align: center
|
||||
:alt: Automatic Accounting feature in Stock Valuation section of Settings page.
|
||||
|
||||
Refer to the :ref:`Expense <inventory/warehouses_storage/expense-account>` and :ref:`Stock
|
||||
input/output <inventory/warehouses_storage/stock-account>` sections of documentation for details on
|
||||
configuring the accounting journals shown.
|
||||
|
||||
.. _inventory/warehouses_storage/valuation-on-product-category:
|
||||
|
||||
Product category setup
|
||||
----------------------
|
||||
|
||||
After :ref:`enabling inventory valuation <inventory/warehouses_storage/accounting-setup>`, the next
|
||||
step is to set the product category to use automatic inventory valuation.
|
||||
|
||||
Go to :menuselection:`Inventory app --> Configuration --> Product Categories`, and select the
|
||||
desired product category. In the :guilabel:`Inventory Valuation` section, set the
|
||||
:guilabel:`Inventory Valuation` field to :guilabel:`Automated`. Repeat this step for every product
|
||||
category intending to use automatic inventory valuation.
|
||||
|
||||
.. note::
|
||||
After enabling automatic accounting, each new stock move layer (SVL), that is created during
|
||||
inventory valuation updates, generates a journal entry.
|
||||
|
||||
.. image:: inventory_valuation_config/automated-inventory-valuation.png
|
||||
:align: center
|
||||
:alt: Inventory Valuation field on the product category, with its various stock accounts.
|
||||
|
||||
.. _inventory/warehouses_storage/costing_methods:
|
||||
|
||||
Costing method
|
||||
==============
|
||||
|
||||
After :ref:`enabling inventory valuation <inventory/warehouses_storage/accounting-setup>`, the
|
||||
*costing method* for calculating and recording inventory costs is defined on the product category in
|
||||
Odoo.
|
||||
|
||||
Go to :menuselection:`Inventory app --> Configuration --> Product Categories` and select the desired
|
||||
product category. In the :guilabel:`Inventory Valuation` section, select the appropriate
|
||||
:guilabel:`Costing Method`:
|
||||
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: Standard Price
|
||||
|
||||
The default costing method in Odoo. The cost of the product is manually defined on the product
|
||||
form, and this cost is used to compute the valuation. Even if the purchase price on a purchase
|
||||
order differs, the valuation is the cost defined on the product form.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:stub-columns: 1
|
||||
|
||||
* - Operation
|
||||
- Unit Cost
|
||||
- Qty On Hand
|
||||
- Incoming Value
|
||||
- Inventory Value
|
||||
* -
|
||||
- $10
|
||||
- 0
|
||||
-
|
||||
- $0
|
||||
* - Receive 8 products for $10/unit
|
||||
- $10
|
||||
- 8
|
||||
- 8 * $10
|
||||
- $80
|
||||
* - Receive 4 products for $16/unit
|
||||
- $10
|
||||
- 12
|
||||
- 4 * $10
|
||||
- $120
|
||||
* - Deliver 10 products
|
||||
- $10
|
||||
- 2
|
||||
- -10 * $10
|
||||
- $20
|
||||
* - Receive 2 products for $9/unit
|
||||
- $10
|
||||
- 4
|
||||
- 2 * $10
|
||||
- $40
|
||||
|
||||
.. tab:: Average Cost (AVCO)
|
||||
|
||||
Calculates the valuation of a product based on the average cost of that product, divided by
|
||||
the total number of available stock on-hand. With this costing method, inventory valuation is
|
||||
*dynamic*, and constantly adjusts based on the purchase price of products.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:stub-columns: 1
|
||||
|
||||
* - Operation
|
||||
- Unit Cost
|
||||
- Qty On Hand
|
||||
- Incoming Value
|
||||
- Inventory Value
|
||||
* -
|
||||
- $0
|
||||
- 0
|
||||
-
|
||||
- $0
|
||||
* - Receive 8 products for $10/unit
|
||||
- $10
|
||||
- 8
|
||||
- 8 * $10
|
||||
- $80
|
||||
* - Receive 4 products for $16/unit
|
||||
- $12
|
||||
- 12
|
||||
- 4 * $16
|
||||
- $144
|
||||
* - Deliver 10 products
|
||||
- $12
|
||||
- 2
|
||||
- -10 * $12
|
||||
- $24
|
||||
* - Receive 2 products for $6/unit
|
||||
- $9
|
||||
- 4
|
||||
- 2 * $6
|
||||
- $36
|
||||
|
||||
How are unit cost and inventory value calculated at each step?
|
||||
|
||||
- When receiving four products for $16 each:
|
||||
|
||||
- Inventory value is calculated by adding the previous inventory value with the incoming
|
||||
value: :math:`$80 + (4 * $16) = $144`.
|
||||
- Unit cost is calculated by dividing the inventory value by the quantity on-hand:
|
||||
:math:`$144 / 12 = $12`.
|
||||
|
||||
- When delivering ten products, the average unit cost is used to calculate the inventory
|
||||
value, regardless of the purchase price of the product. Therefore, inventory value is
|
||||
:math:`$144 + (-10 * $12) = $24`.
|
||||
|
||||
- Receive two products for $6 each:
|
||||
|
||||
- Inventory value: :math:`$24 + (2 * $6) = $36`
|
||||
- Unit cost: :math:`$36 / 4 = $9`
|
||||
|
||||
.. note::
|
||||
When choosing :guilabel:`Average Cost (AVCO)` as the :guilabel:`Costing Method`, changing
|
||||
the numerical value in the *Cost* field for products in the respective product category
|
||||
creates a new record in the *Inventory Valuation* report to adjust the value of the
|
||||
product. The *Cost* amount is then automatically updated, based on the average purchase
|
||||
price of both the inventory on-hand and the costs accumulated from validated purchase
|
||||
orders.
|
||||
|
||||
.. tab:: First In First Out (FIFO)
|
||||
|
||||
Tracks the costs of incoming and outgoing items in real-time, and uses the real price of the
|
||||
products to change the valuation. The oldest purchase price is used as the cost for the next
|
||||
good sold, until an entire lot of that product is sold. When the next inventory lot moves up
|
||||
in the queue, an updated product cost is used based on the valuation of that specific lot.
|
||||
|
||||
This method is arguably the most accurate inventory valuation method for a variety of reasons,
|
||||
but it is highly sensitive to input data and human error.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:stub-columns: 1
|
||||
|
||||
* - Operation
|
||||
- Unit Cost
|
||||
- Qty On Hand
|
||||
- Incoming Value
|
||||
- Inventory Value
|
||||
* -
|
||||
- $0
|
||||
- 0
|
||||
-
|
||||
- $0
|
||||
* - Receive 8 products for $10/unit
|
||||
- $10
|
||||
- 8
|
||||
- 8 * $10
|
||||
- $80
|
||||
* - Receive 4 products for $16/unit
|
||||
- $12
|
||||
- 12
|
||||
- 4 * $16
|
||||
- $144
|
||||
* - Deliver 10 products
|
||||
- $16
|
||||
- 2
|
||||
- | -8 * $10
|
||||
| -2 * $16
|
||||
- $32
|
||||
* - Receive 2 products for $6/unit
|
||||
- $11
|
||||
- 4
|
||||
- 2 * $6
|
||||
- $44
|
||||
|
||||
How are unit cost and inventory value calculated at each step?
|
||||
|
||||
- When receiving four products for $16 each:
|
||||
|
||||
- Inventory value is calculated by adding the previous inventory value to the incoming
|
||||
value: :math:`$80 + (4 * $16) = $144`.
|
||||
- Unit cost is calculated by dividing the inventory value by the quantity on-hand:
|
||||
:math:`$144 / 12 = $12`.
|
||||
|
||||
- When delivering ten products, eight units were purchased for $10, and two units were
|
||||
purchased for $16.
|
||||
|
||||
- First, the incoming value is calculated by multiplying the on-hand quantity by the
|
||||
purchased price: :math:`(-8 * $10) + (-2 * $16) = -112`.
|
||||
- The inventory value is calculated by subtracting the incoming value from the previous
|
||||
inventory value: :math:`$144 - $112 = $32`.
|
||||
- Unit cost is calculated by dividing the inventory value by the remaining quantity:
|
||||
:math:`$32 / 2 = $16`.
|
||||
|
||||
- When receiving two products for $6, inventory value is :math:`$32 + $12 = $44`. Unit cost is
|
||||
:math:`$44 / 4 = $11`.
|
||||
|
||||
.. warning::
|
||||
Changing the costing method greatly impacts inventory valuation. It is highly recommended to
|
||||
consult an accountant first before making any adjustments here.
|
||||
|
||||
.. seealso::
|
||||
:doc:`using_inventory_valuation`
|
||||
|
||||
When the :guilabel:`Costing Method` is changed, products already in stock that were using the
|
||||
:guilabel:`Standard` costing method **do not** change value; rather, the existing units keep their
|
||||
value, and any product moves from then on affect the average cost, and the cost of the product will
|
||||
change. If the value in the :guilabel:`Cost` field on a product form is changed manually, Odoo
|
||||
generates a corresponding record in the *Inventory Valuation* report.
|
||||
|
||||
.. note::
|
||||
It is possible to use different valuation settings for different product categories.
|
||||
|
||||
.. _inventory/warehouses_storage/accounting-types:
|
||||
|
||||
Types of accounting
|
||||
===================
|
||||
|
||||
With automated inventory valuation set up, the generated journal entries depend on the chosen
|
||||
accounting mode: *Continental* or *Anglo-Saxon*.
|
||||
|
||||
.. tip::
|
||||
Verify the accounting mode by activating the :ref:`developer-mode`, and navigating to
|
||||
:menuselection:`Accounting app --> Configuration --> Settings`.
|
||||
|
||||
Then, in the :guilabel:`Search...` bar, look for `Anglo-Saxon Accounting`, to see if the feature
|
||||
is enabled. If it is **not** enabled, *Continental* accounting mode is in use.
|
||||
|
||||
.. image:: inventory_valuation_config/anglo-saxon.png
|
||||
:align: center
|
||||
:alt: Show the Anglo-Saxon accounting mode feature.
|
||||
|
||||
In *Anglo-Saxon* accounting, the costs of goods sold (COGS) are reported when products are sold or
|
||||
delivered. This means the cost of a good is only recorded as an expense when a customer is invoiced
|
||||
for a product.
|
||||
|
||||
So, for **manual** valuation method, set the *Expense Account* to *Stock Valuation* for the current
|
||||
asset type; for **automatic** valuation method, set the *Expense Account* to an *Expenses* or a
|
||||
*Cost of Revenue* type (e.g. *Cost of Production*, *Cost of Goods Sold*, etc.).
|
||||
|
||||
In *Continental* accounting, the cost of a good is reported as soon as a product is received into
|
||||
stock. Because of this, the *Expense Account* can be set to **either** *Expenses* or a *Cost of
|
||||
Revenue* type, however, it is more commonly set to an *Expenses* account.
|
||||
|
||||
Refer to the :ref:`Expense <inventory/warehouses_storage/expense-account>` and :ref:`Stock
|
||||
input/output <inventory/warehouses_storage/stock-account>` sections for details on configuring each
|
||||
account type.
|
||||
|
||||
.. _inventory/warehouses_storage/expense-account:
|
||||
|
||||
Expense account
|
||||
---------------
|
||||
|
||||
To configure the *expense account*, which is used in both manual and automatic inventory valuation,
|
||||
go to the :guilabel:`Account Properties` section of the intended product category
|
||||
(:menuselection:`Inventory app --> Configuration --> Product Categories`). Then, choose an existing
|
||||
account from the :guilabel:`Expense Account` drop-down menu.
|
||||
|
||||
To ensure the chosen account is the correct :guilabel:`Type,` click the |right arrow| icon to the
|
||||
right of the account. Then, set the account type based on the information below.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Anglo-Saxon
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Automated
|
||||
|
||||
In Anglo-Saxon accounting for automated inventory valuation, set the :guilabel:`Expense
|
||||
Account` to the `Expenses` account. Then, click the |right arrow| icon to the right of
|
||||
the account.
|
||||
|
||||
In the pop-up window, choose :guilabel:`Expenses` or :guilabel:`Cost of Revenue` from
|
||||
the :guilabel:`Type` drop-down menu.
|
||||
|
||||
.. image:: inventory_valuation_config/external-link.png
|
||||
:align: center
|
||||
:alt: Show **Expense Account** field, and external link icon.
|
||||
|
||||
.. group-tab:: Manual
|
||||
|
||||
To configure the :guilabel:`Expense Account`, choose :guilabel:`Stock Valuation` from
|
||||
the field's drop-down menu. Verify the account's type by clicking the |right arrow|
|
||||
icon, and then ensure the :guilabel:`Type` is :guilabel:`Current Assets`.
|
||||
|
||||
.. image:: inventory_valuation_config/manual-anglo-saxon-expense.png
|
||||
:align: center
|
||||
:alt: Show the **Expense Account** field.
|
||||
|
||||
.. group-tab:: Continental
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Automated
|
||||
|
||||
Set the :guilabel:`Expense Account` to the :guilabel:`Expenses` or :guilabel:`Cost of
|
||||
Revenue` account type.
|
||||
|
||||
.. group-tab:: Manual
|
||||
|
||||
Set the :guilabel:`Expense Account` to the :guilabel:`Expenses` or :guilabel:`Cost of
|
||||
Revenue` account type.
|
||||
|
||||
.. _inventory/warehouses_storage/stock-account:
|
||||
|
||||
Stock input/output (automated only)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To configure the :guilabel:`Stock Input Account` and :guilabel:`Stock Output Account`, go to
|
||||
:menuselection:`Inventory app --> Configuration --> Product Categories` and select the desired
|
||||
product category.
|
||||
|
||||
In the :guilabel:`Inventory Valuation` field, select :guilabel:`Automated`. Doing so makes the
|
||||
:guilabel:`Account Stock Properties` section appear. These accounts are defined as follows:
|
||||
|
||||
- :guilabel:`Stock Valuation Account`: when automated inventory valuation is enabled on a product,
|
||||
this account will hold the current value of the products.
|
||||
- :guilabel:`Stock Journal`: accounting journal where entries are automatically posted when a
|
||||
product's inventory valuation changes.
|
||||
- :guilabel:`Stock Input Account`: counterpart journal items for all incoming stock moves will be
|
||||
posted in this account, unless there is a specific valuation account set on the source location.
|
||||
This is the default value for all products in a given category, and can also be set directly on
|
||||
each product.
|
||||
- :guilabel:`Stock Output Account`: counterpart journal items for all outgoing stock moves will be
|
||||
posted in this account, unless there is a specific valuation account set on the destination
|
||||
location. This is the default value for all products in a given category, and can also be set
|
||||
directly on each product.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Anglo-Saxon
|
||||
|
||||
In Anglo-Saxon accounting, the :guilabel:`Stock Input Account` and :guilabel:`Stock Output
|
||||
Account` are set to *different* :guilabel:`Current Assets` accounts. This way, delivering
|
||||
products and invoicing the customer balance the *Stock Output* account, while receiving
|
||||
products and billing vendors balance the *Stock Input* account.
|
||||
|
||||
To modify the account type, go to the click the |right arrow| icon to the right of the stock
|
||||
input/output account. In the pop-up window, choose :guilabel:`Current Assets` from the
|
||||
:guilabel:`Type` drop-down menu.
|
||||
|
||||
.. figure:: inventory_valuation_config/account-type.png
|
||||
:align: center
|
||||
:alt: Display account setup page, highlighting the **Type** field.
|
||||
|
||||
The *Stock Input* account is set to `Stock Interim (Received)`, a *Current Asset* account
|
||||
type.
|
||||
|
||||
.. group-tab:: Continental
|
||||
|
||||
In Continental accounting, the :guilabel:`Stock Input Account` and :guilabel:`Stock Output
|
||||
Account` are set to **the same** :guilabel:`Current Assets` account. That way, one account can
|
||||
be balanced when items are bought and sold.
|
||||
|
||||
.. example::
|
||||
The stock input and output accounts are both set to `Stock Interim (Received)`, a
|
||||
:guilabel:`Current Assets` account type. They can also be set to the `Stock Interim
|
||||
(Delivered)`, as long as the input and output accounts are assigned to the **same**
|
||||
account.
|
||||
|
||||
.. image:: inventory_valuation_config/continental-stock-account.png
|
||||
:align: center
|
||||
:alt: Show the Stock Input and Output accounts.
|
||||
|
||||
Inventory valuation reporting
|
||||
=============================
|
||||
|
||||
To start, go to :menuselection:`Accounting app --> Reporting --> Balance Sheet`. Click the
|
||||
:guilabel:`Current Assets` line item to unfold the drop-down menu, and look for the nested
|
||||
:guilabel:`Stock Valuation`, :guilabel:`Stock Interim (Received)`, and :guilabel:`Stock Interim
|
||||
(Delivered)` lines.
|
||||
|
||||
.. tip::
|
||||
At the top of the dashboard, click the :guilabel:`As of [date]` button to display accounting
|
||||
records up to a specified date.
|
||||
|
||||
.. seealso::
|
||||
- :ref:`Stock accounts and what they do <inventory/warehouses_storage/stock-account>`
|
||||
- :doc:`../../../../finance/accounting/get_started/cheat_sheet`
|
||||
|
||||
.. image:: inventory_valuation_config/stock-balance-sheet.png
|
||||
:align: center
|
||||
:alt: See the full inventory valuation breakdown in Odoo Accounting app.
|
||||
|
||||
Access more specific information by clicking the :icon:`fa-ellipsis-v` :guilabel:`(ellipsis)` icon
|
||||
to the right of the desired journal. Select :guilabel:`General Ledger` to see a list of all of the
|
||||
journal entries, where each line item's :icon:`fa-ellipsis-v` :guilabel:`(ellipsis)` icon can be
|
||||
clicked to reveal the :guilabel:`View Journal Entry` option to open the individualized journal
|
||||
entry.
|
||||
|
||||
Additionally, annotations to the :guilabel:`Balance Sheet` can be added by choosing
|
||||
:guilabel:`Annotate`, filling in the text box, and clicking :guilabel:`Save`.
|
||||
|
||||
.. image:: inventory_valuation_config/journals.png
|
||||
:align: center
|
||||
:alt: Show Stock Valuation journals in a list.
|
||||
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 27 KiB |
@@ -1,178 +0,0 @@
|
||||
=========================
|
||||
Using inventory valuation
|
||||
=========================
|
||||
|
||||
.. _inventory/reporting/using_inventory_val:
|
||||
|
||||
*Inventory valuation* is a quintessential accounting procedure that calculates the value of on-hand
|
||||
stock. Once determined, the inventory valuation amount is then incorporated into a company's overall
|
||||
value.
|
||||
|
||||
In Odoo, this process can be conducted manually— by warehouse employees physically counting the
|
||||
products— or automatically through the database.
|
||||
|
||||
Automatic inventory valuation
|
||||
=============================
|
||||
|
||||
To use Odoo to automatically generate a trail of inventory valuation entries, first navigate to the
|
||||
:menuselection:`Product Categories` list by going to :menuselection:`Inventory app --> Configuration
|
||||
--> Product Categories` and select the desired product category. On the form, set the
|
||||
:guilabel:`Inventory Valuation` as :guilabel:`Automated` and the :guilabel:`Costing Method` to any
|
||||
of the three options.
|
||||
|
||||
.. seealso::
|
||||
:doc:`Set up inventory valuation <inventory_valuation_config>`
|
||||
|
||||
In order to understand how moving products in and out of stock affects the company's overall value,
|
||||
consider the following product and stock moves scenario below.
|
||||
|
||||
Receive a product
|
||||
-----------------
|
||||
|
||||
To track the value of incoming products, such as a simple *table*, configure the product category on
|
||||
the the product itself. To get there, navigate to :menuselection:`Inventory app --> Products -->
|
||||
Products` and click the desired product. On the product form, click the :guilabel:`➡️ (right arrow)`
|
||||
icon beside the :guilabel:`Product Category` field, which opens an internal link to edit the product
|
||||
category. Next, set the :guilabel:`Costing Method` as :guilabel:`First In First Out (FIFO)` and
|
||||
:guilabel:`Inventory Valuation` as :guilabel:`Automated`.
|
||||
|
||||
.. tip::
|
||||
Alternatively access the :guilabel:`Product Categories` dashboard by navigating to
|
||||
:menuselection:`Inventory app --> Configuration --> Product Categories` and select the desired
|
||||
product category.
|
||||
|
||||
Next, assume 10 tables are purchased at a price of $10.00, each. The :abbr:`PO (Purchase Order)` for
|
||||
those tables will show the subtotal of the purchase as $100, plus any additional costs or taxes.
|
||||
|
||||
.. image:: using_inventory_valuation/purchase-order.png
|
||||
:align: center
|
||||
:alt: Purchase order with 10 tables products valued at $10.00 each.
|
||||
|
||||
After selecting :guilabel:`Validate` on the :abbr:`PO (Purchase Order)`, the :guilabel:`Valuation`
|
||||
smart button is enabled. Clicking on this button displays a report showing how the inventory
|
||||
valuation for the table was affected by this purchase.
|
||||
|
||||
.. important::
|
||||
:ref:`Developer mode <developer-mode>` **must** be turned on to see the :guilabel:`Valuation`
|
||||
smart button.
|
||||
|
||||
.. tip::
|
||||
The :doc:`consignment <../../shipping_receiving/daily_operations/owned_stock>` feature allows
|
||||
ownership to items in stock. Thus, products owned by other companies are not accounted for in the
|
||||
host company's inventory valuation.
|
||||
|
||||
.. image:: using_inventory_valuation/valuation-smart-button.png
|
||||
:align: center
|
||||
:alt: See Valuation smart button on a receipt, with Developer mode enabled.
|
||||
|
||||
For a comprehensive dashboard that includes the inventory valuation of all product shipments,
|
||||
inventory adjustments, and warehouse operations, refer to the :ref:`stock valuation report
|
||||
<inventory/management/reporting/valuation-report>`.
|
||||
|
||||
Deliver a product
|
||||
-----------------
|
||||
|
||||
In the same logic, when a table is shipped to a customer and leaves the warehouse, the stock
|
||||
valuation decreases. The :guilabel:`Valuation` smart button on the :abbr:`DO (Delivery Order)`,
|
||||
likewise, displays the stock valuation record as it does on a :abbr:`PO (Purchase Order)`.
|
||||
|
||||
.. image:: using_inventory_valuation/decreased-stock-valuation.png
|
||||
:align: center
|
||||
:alt: Decreased stock valuation after a product is shipped.
|
||||
|
||||
.. _inventory/management/reporting/valuation-report:
|
||||
|
||||
Inventory valuation report
|
||||
==========================
|
||||
|
||||
To view the current value of all products in the warehouse, first turn on :ref:`Developer mode
|
||||
<developer-mode>` and navigate to :menuselection:`Inventory app --> Reporting --> Valuation`. The
|
||||
:guilabel:`Stock Valuation` dashboard displays detailed records of products with the
|
||||
:guilabel:`Date`, :guilabel:`Quantity`, :guilabel:`Unit Value`, and :guilabel:`Total Value` of the
|
||||
inventory.
|
||||
|
||||
.. important::
|
||||
:ref:`Developer mode <developer-mode>` **must** be enabled to see the :guilabel:`Valuation`
|
||||
option under :guilabel:`Reporting`.
|
||||
|
||||
.. image:: using_inventory_valuation/inventory-valuation-products.png
|
||||
:align: center
|
||||
:alt: Inventory valuation report showing multiple products.
|
||||
|
||||
The :guilabel:`Valuation At Date` button, located in the top-left corner of the :guilabel:`Stock
|
||||
Valuation` page, reveals a pop-up window. In this pop-up, the inventory valuation of products
|
||||
available during a prior specified date can be seen and selected.
|
||||
|
||||
.. tip::
|
||||
View a detailed record of a product's inventory value, stock move, and on-hand stock by selecting
|
||||
the teal :guilabel:`➡️ (right arrow)` button to the right of the :guilabel:`Reference` column
|
||||
value.
|
||||
|
||||
.. _inventory/product_management/update-unit-price:
|
||||
|
||||
Update product unit price
|
||||
-------------------------
|
||||
|
||||
For any company: lead times, supply chain failures, and other risk factors can contribute to
|
||||
invisible costs. Although Odoo attempts to accurately represent the stock value, *manual valuation*
|
||||
serves as an additional tool to update the unit price of products.
|
||||
|
||||
.. important::
|
||||
Manual valuation is intended for products that can be purchased and received for a cost greater
|
||||
than 0, or have product categories set with :guilabel:`Costing Method` set as either
|
||||
:guilabel:`Average Cost (AVCO)` or :guilabel:`First In First Out (FIFO)`.
|
||||
|
||||
.. image:: using_inventory_valuation/add-manual-valuation.png
|
||||
:align: center
|
||||
:alt: Add manual valuation of stock value to a product.
|
||||
|
||||
Create manual valuation entries on the :guilabel:`Stock Valuation` dashboard by first navigating to
|
||||
:menuselection:`Inventory app --> Reporting --> Valuation`. Next, to enable the *product
|
||||
revaluation* feature, select :menuselection:`Group by --> Product` to organize all the records by
|
||||
product. Click on the gray :guilabel:`▶️ (drop-down triangle)` icon to reveal stock valuation line
|
||||
items below, as well as a teal :guilabel:`➕ (plus)` button on the right.
|
||||
|
||||
Click the teal :guilabel:`+ (plus)` button to open up the :guilabel:`Product Revaluation` form.
|
||||
Here, the inventory valuation for a product can be recalculated, by increasing or decreasing the
|
||||
unit price of each product.
|
||||
|
||||
.. note::
|
||||
The :guilabel:`▶️ (drop-down triangle)` and :guilabel:`➕ (plus)` buttons are only visible after
|
||||
grouping entries by product.
|
||||
|
||||
.. image:: using_inventory_valuation/product-revaluation.png
|
||||
:align: center
|
||||
:alt: Product revaluation form adding a value of $1.00 with the reason being inflation.
|
||||
|
||||
Inventory valuation journal entries
|
||||
-----------------------------------
|
||||
|
||||
In Odoo, automatic inventory valuation records are also recorded in the :menuselection:`Accounting
|
||||
app --> Accounting --> Journal Entries` dashboard. On this comprehensive list of accounting entries,
|
||||
inventory valuation records are identified by checking values in the :guilabel:`Journal` column, or
|
||||
looking for the :guilabel:`Reference` column value which matches the warehouse operation reference
|
||||
(e.g. `WH/IN/00014` for receipts).
|
||||
|
||||
Clicking on an inventory valuation journal entry opens a *double-entry accounting* record. These
|
||||
records are generated by Odoo to track the change of value in inventory valuation as products are
|
||||
moved in and out of the warehouse.
|
||||
|
||||
.. example::
|
||||
To view the inventory valuation of 10 *tables*, costing $10.00 each, upon reception from the
|
||||
vendor, go to the :menuselection:`Journal Entries` page found in :menuselection:`Accounting app
|
||||
--> Accounting --> Journal Entries`. Here, click the journal line where the :guilabel:`Reference`
|
||||
column value matches the reference on the receipt, `WH/IN/00014`.
|
||||
|
||||
.. image:: using_inventory_valuation/stock-valuation-product.png
|
||||
:align: center
|
||||
:alt: Stock valuation page depicting the products within a shipment.
|
||||
|
||||
`Stock interim` is a holding account for money intended to pay vendors for the product. The
|
||||
`stock valuation` account stores the value of all on-hand stock.
|
||||
|
||||
.. image:: using_inventory_valuation/inventory-valuation-entry.png
|
||||
:align: center
|
||||
:alt: Accounting entry for the inventory valuation of 10 tables.
|
||||
|
||||
.. seealso::
|
||||
`Odoo Tutorial: Inventory Valuation <https://www.odoo.com/slides/slide/2795/share>`_
|
||||
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 14 KiB |
@@ -64,8 +64,7 @@ field, there are:
|
||||
View locations should **not** contain products, but it is possible to move them there.
|
||||
|
||||
- :guilabel:`Internal Location`: storage locations within the warehouse. Items stored in these
|
||||
locations are accounted for in :doc:`inventory valuation
|
||||
<../product_management/inventory_valuation/using_inventory_valuation>`.
|
||||
locations are accounted for in :doc:`inventory valuation <../inventory_valuation/cheat_sheet>`.
|
||||
|
||||
- :guilabel:`Customer Location`: where sold products are tracked; items here are no longer in stock.
|
||||
|
||||
@@ -110,10 +109,9 @@ Odoo databases include preconfigured view locations to organize the hierarchy of
|
||||
provide helpful context, and distinguish between internal and external locations.
|
||||
|
||||
- *Physical locations* group internal locations—such as secondary warehouses and subcontractor
|
||||
sites. Because :doc:`inventory valuation
|
||||
<../product_management/inventory_valuation/inventory_valuation_config>` changes only when goods
|
||||
move from internal to external locations, Odoo uses physical locations to track stock that is
|
||||
off-site or in transit without affecting valuation.
|
||||
sites. Because :doc:`inventory valuation <../inventory_valuation/cheat_sheet>` changes only when
|
||||
goods move from internal to external locations, Odoo uses physical locations to track stock that
|
||||
is off-site or in transit without affecting valuation.
|
||||
|
||||
.. _inventory/warehouses_storage/interwarehouse-transit:
|
||||
|
||||
|
||||
@@ -422,7 +422,7 @@ Example where visibility days is triggered
|
||||
------------------------------------------
|
||||
|
||||
A product shipped from Asia has a combined vendor lead time of 30 days and a shipping cost of $100
|
||||
(including :doc:`landed costs <../../product_management/inventory_valuation/landed_costs>` and
|
||||
(including :doc:`landed costs <../../inventory_valuation/landed_costs>` and
|
||||
tariffs).
|
||||
|
||||
- November 4: Current date. The forecasted date is December 4 (30 days later).
|
||||
|
||||
@@ -11,8 +11,8 @@ assigning a monetary value to account for inventory is known as *stock valuation
|
||||
This value is often reported for accounting purposes. For instance, an insurance company may want to
|
||||
know the value of goods stored in a warehouse, in the event of a flood or fire.
|
||||
|
||||
:doc:`Stock valuation <../../product_management/inventory_valuation/using_inventory_valuation>`
|
||||
typically utilizes one of two accounting systems:
|
||||
:doc:`Stock valuation <../../inventory_valuation/cheat_sheet>` typically utilizes one of two
|
||||
accounting systems:
|
||||
|
||||
- **Perpetual**: The inventory is constantly (perpetually) being updated, and the value is
|
||||
constantly changing.
|
||||
@@ -21,11 +21,10 @@ typically utilizes one of two accounting systems:
|
||||
|
||||
Using :ref:`tracked inventory <inventory/product_management/tracking-inventory>` in Odoo
|
||||
necessitates a *perpetual* inventory accounting system because of the need to know when and where
|
||||
inventory exists, and how much of it is available or forecasted. There are a few common :ref:`stock
|
||||
valuation methods <inventory/warehouses_storage/costing_methods>` used in Odoo: *standard price*,
|
||||
*average cost* (AVCO), and *first in, first out* (FIFO) accounting. It is important to know that the
|
||||
valuation method chosen for a product impacts the calculation of several fields in the stock
|
||||
valuation reports.
|
||||
inventory exists, and how much of it is available or forecasted. There are a few common valuation
|
||||
methods used in Odoo: *standard price*, *average cost* (AVCO), and *first in, first out* (FIFO)
|
||||
accounting. It is important to know that the valuation method chosen for a product impacts the
|
||||
calculation of several fields in the stock valuation reports.
|
||||
|
||||
Open the dashboard
|
||||
==================
|
||||
|
||||
@@ -36,8 +36,7 @@ In the report itself, the columns represent:
|
||||
|
||||
.. seealso::
|
||||
- :ref:`Compute average cost inventory valuation per unit <inventory/avg_cost/formula>`
|
||||
- :doc:`Inventory valuation methods
|
||||
<../../product_management/inventory_valuation/inventory_valuation_config>`
|
||||
- :doc:`Inventory valuation methods <../../inventory_valuation/cheat_sheet>`
|
||||
|
||||
- :guilabel:`On Hand`: current quantity of products. Click the :icon:`fa-pencil`
|
||||
:guilabel:`(pencil)` icon to :doc:`modify the on-hand quantity
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# applications/websites
|
||||
|
||||
applications/websites/ecommerce/payments.rst applications/websites/ecommerce/checkout.rst
|
||||
|
||||
# applications/inventory_and_mrp
|
||||
|
||||
applications/inventory_and_mrp/inventory/product_management/inventory_valuation/landed_costs.rst applications/inventory_and_mrp/inventory/inventory_valuation/landed_costs.rst
|
||||
applications/inventory_and_mrp/inventory/product_management/inventory_valuation/inventory_valuation_config.rst applications/inventory_and_mrp/inventory/inventory_valuation/cheat_sheet.rst
|
||||
applications/inventory_and_mrp/inventory/product_management/inventory_valuation/using_inventory_valuation.rst applications/inventory_and_mrp/inventory/inventory_valuation/cheat_sheet.rst
|
||||
applications/inventory_and_mrp/inventory/product_management/inventory_valuation/valuation_by_lots.rst applications/inventory_and_mrp/inventory/inventory_valuation/valuation_by_lots.rst
|
||||
|
||||
@@ -106,13 +106,6 @@ label:hover,
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.values-table tr > * {
|
||||
text-align: right;
|
||||
}
|
||||
.values-table tr > :first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* 3-column (thing, debit, credit) tables */
|
||||
/* 2nd and 3rd th & td of each row right-aligned and 1/4th width */
|
||||
.d-c-table tr > :nth-child(2),
|
||||
@@ -139,21 +132,7 @@ label:hover,
|
||||
background-color: #eee !important;
|
||||
color: #7A436B !important;
|
||||
}
|
||||
|
||||
.chart-of-accounts .highlight-op,
|
||||
.valuation-chart .highlight-op {
|
||||
background-color: #030035;
|
||||
border-bottom: 1px solid #000000 !important;
|
||||
}
|
||||
|
||||
.chart-of-accounts .highlight-op,
|
||||
.valuation-chart-continental .highlight-op {
|
||||
background-color: #030035;
|
||||
border-bottom: 1px solid #000000 !important;
|
||||
}
|
||||
|
||||
.chart-of-accounts .highlight-op,
|
||||
.valuation-chart-anglo-saxon .highlight-op {
|
||||
.chart-of-accounts .highlight-op {
|
||||
background-color: #030035;
|
||||
border-bottom: 1px solid #000000 !important;
|
||||
}
|
||||
@@ -217,17 +196,3 @@ blockquote.highlights, blockquote.highlights p{
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*
|
||||
lists of alternatives
|
||||
*/
|
||||
.alternatives-controls label {
|
||||
display: block;
|
||||
}
|
||||
dl.alternatives > dt,
|
||||
dl.alternatives > dd {
|
||||
display: none;
|
||||
}
|
||||
dl.alternatives > dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
128
static/css/valuation.css
Normal file
@@ -0,0 +1,128 @@
|
||||
/* Used in valuation cheat_sheet.rst */
|
||||
|
||||
/* Prevent titles from wrapping upwards */
|
||||
h3 { clear: both !important; }
|
||||
|
||||
.full-width { width: 100% !important; }
|
||||
|
||||
.accounting-entries, .journal-entries, .values-table {
|
||||
border: 1px solid #d5d5d5;
|
||||
background-color: #f8f8f8;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
/* Costing methods table - see misc.js */
|
||||
.alternatives-controls {
|
||||
padding-bottom: 24px;
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
dl.alternatives > dt {
|
||||
display: none;
|
||||
}
|
||||
dl.alternatives > dd {
|
||||
display: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
.values-table {
|
||||
text-align: right;
|
||||
tr > th:first-of-type {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/* Accounting methods table - see valuation-accounting.js */
|
||||
.accounting-entries {
|
||||
th, td {
|
||||
padding-top: 0px !important;
|
||||
padding-bottom: 0px !important;
|
||||
}
|
||||
thead th, tr > td {
|
||||
padding-left: 48px;
|
||||
}
|
||||
tbody th {
|
||||
font-weight: normal;
|
||||
}
|
||||
.parent-line { background-color: #fafafa; }
|
||||
.child-line { background-color: #f0f0f0; }
|
||||
}
|
||||
.entries-listing {
|
||||
padding: .5rem;
|
||||
}
|
||||
#accounting-entries-controls label,
|
||||
#journaling-entries-controls label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Highlighting of selected options - see misc.js */
|
||||
label:hover, .highlighter-list li:hover {
|
||||
background-color: hsl(0, 0%, 94%);
|
||||
cursor: pointer;
|
||||
}
|
||||
.related {
|
||||
background-color: hsl(317deg 16% 90%) !important;
|
||||
border: 1px solid #000000 !important;
|
||||
transition: .3s;
|
||||
}
|
||||
.secondary {
|
||||
background-color: hsl(180deg 67% 94%) !important;
|
||||
transition: .3s;
|
||||
}
|
||||
.highlight-op {
|
||||
background-color: hsl(317deg 16% 90%) !important;
|
||||
transition: .3s;
|
||||
}
|
||||
.highlighter-target {
|
||||
th {
|
||||
font-weight: 400;
|
||||
}
|
||||
.related {
|
||||
/*background-color: #eee !important;*/
|
||||
color: #7A436B !important;
|
||||
}
|
||||
.secondary {
|
||||
background-color: #eee !important;
|
||||
color: #7A436B !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Static tables */
|
||||
.feature-table, .config-table {
|
||||
table {
|
||||
/* width: unset; */
|
||||
margin: auto;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
tbody td:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.config-table {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.accounting-app-paragraph:hover ~ .feature-table tr > td:first-child:has(+ td .good),
|
||||
.inventory-app-paragraph:hover ~ .feature-table tr > td:first-child:has(+ td + td .good)
|
||||
{ font-weight: bold; }
|
||||
|
||||
.feature-table {
|
||||
td { width: 32px; }
|
||||
td:has(.good) { background-color: #d9ead3 !important; }
|
||||
td:has(.meh) { background-color: #fce5cd !important; }
|
||||
td:has(.bad) { background-color: #f4cccc !important; }
|
||||
}
|
||||
.config-table {
|
||||
th, td {
|
||||
padding-top: 0px !important;
|
||||
padding-bottom: 0px !important;
|
||||
}
|
||||
td:has(.washed) { color: var(--bs-gray); }
|
||||
}
|
||||
|
||||
.yellow, td:has(.yellow), th:has(.yellow) { background-color: #fff2cc !important; }
|
||||
.green, td:has(.green), th:has(.green) { background-color: #d9ead3 !important; }
|
||||
.blue, td:has(.blue), th:has(.blue) { background-color: #cfe2f3 !important; }
|
||||
.darkblue, td:has(.darkblue), th:has(.darkblue) { background-color: #6d9eeb !important; }
|
||||
.purple, td:has(.purple), th:has(.purple) { background-color: #d9d2e9 !important; }
|
||||
@@ -1,6 +1,6 @@
|
||||
/* global Immutable, React */
|
||||
(function () {
|
||||
// NOTE: used by cheat_sheet.rst
|
||||
// NOTE: used by accounting cheat_sheet.rst
|
||||
'use strict';
|
||||
|
||||
function highlight(primary, secondary) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* global Immutable, React */
|
||||
/* global createAtom */
|
||||
(function () {
|
||||
// NOTE: used by cheat_sheet.rst
|
||||
// NOTE: used by accounting cheat_sheet.rst
|
||||
'use strict';
|
||||
|
||||
var data = createAtom();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* global createAtom, findAncestor */
|
||||
(function () {
|
||||
'use strict';
|
||||
// NOTE: cheat_sheet.rst
|
||||
// NOTE: used by accounting cheat_sheet.rst
|
||||
|
||||
var data = createAtom();
|
||||
data.addWatch('chart', function (k, m, prev, next) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
});
|
||||
|
||||
function highlight() {
|
||||
// NOTE: used by double-entry.rst
|
||||
// NOTE: used by valuation cheat_sheet.rst
|
||||
$('.highlighter-list').each(function () {
|
||||
var $this = $(this),
|
||||
$target = $($this.data('target'));
|
||||
@@ -34,7 +34,7 @@
|
||||
* - automatically select first control on startup
|
||||
*/
|
||||
function alternatives() {
|
||||
// NOTE: used by double-entry.rst & valuation_methods pages
|
||||
// NOTE: used by valuation cheat_sheet.rst
|
||||
$('dl.alternatives').each(function (index) {
|
||||
var $list = $(this),
|
||||
$contents = $list.children('dd');
|
||||
@@ -51,7 +51,18 @@
|
||||
|
||||
label.appendChild(input);
|
||||
label.appendChild(document.createTextNode(' '));
|
||||
label.appendChild(document.createTextNode(this.textContent));
|
||||
|
||||
// Hack to bold the definition since we have to strip rST formatting
|
||||
const [headText, tailText] = this.textContent.split(':', 2);
|
||||
if (tailText) {
|
||||
const bold = document.createElement('b'),
|
||||
defined = document.createTextNode(`${headText}:`);
|
||||
bold.appendChild(defined);
|
||||
label.appendChild(bold);
|
||||
}
|
||||
|
||||
label.appendChild(document.createTextNode(tailText || headText));
|
||||
label.normalize();
|
||||
|
||||
return label;
|
||||
}))
|
||||
@@ -65,9 +76,10 @@
|
||||
})
|
||||
.find('input:first').click();
|
||||
});
|
||||
$('.alternatives-note').insertAfter($('.alternatives-controls'));
|
||||
}
|
||||
function checks_handling() {
|
||||
// NOTE: used by cheat_sheet.rst
|
||||
// NOTE: used by accounting cheat_sheet.rst
|
||||
var $section = $('.checks-handling');
|
||||
if (!$section.length) { return; }
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(function () {
|
||||
// NOTE: cheat_sheet.rst
|
||||
// NOTE: used by accounting cheat_sheet.rst
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var $rec = $('#reconciliation .reconciliation-example');
|
||||
if (!$rec.length) { return; }
|
||||
|
||||
262
static/js/valuation-accounting.js
Normal file
@@ -0,0 +1,262 @@
|
||||
/* global Immutable, React */
|
||||
/* global createAtom */
|
||||
/* global VALUATION_{STANDARDS,METHODS,JOURNALS,ENTRIES,REVIEWS} */
|
||||
(function () {
|
||||
'use strict';
|
||||
// NOTE: used by valuation cheat_sheet.rst
|
||||
|
||||
const selectedMode = createAtom(['continental', 'periodic']);
|
||||
const selectedOps = createAtom();
|
||||
|
||||
function watch (next) {
|
||||
React.render(
|
||||
React.createElement(Controls, { p: next }),
|
||||
document.getElementById('accounting-entries-controls'));
|
||||
React.render(
|
||||
React.createElement(Chart, { p: next }),
|
||||
document.querySelector('.accounting-entries'));
|
||||
}
|
||||
|
||||
selectedOps.addWatch('chart', (k, m, prev, next) => watch(next));
|
||||
selectedMode.addWatch('chart', (k, m, prev, next) => watch(selectedOps.deref()));
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const chart = document.querySelector('.accounting-entries');
|
||||
if (!chart) { return; }
|
||||
|
||||
const controls = document.createElement('div');
|
||||
controls.setAttribute('id', 'accounting-entries-controls');
|
||||
chart.parentNode.insertBefore(controls, chart);
|
||||
|
||||
selectedOps.reset(Immutable.Map({
|
||||
// last-selected operation
|
||||
active: null,
|
||||
// set of all currently enabled operations
|
||||
operations: Immutable.OrderedSet()
|
||||
}));
|
||||
});
|
||||
|
||||
function toKey(s, postfix) {
|
||||
if (postfix) {
|
||||
s += ' ' + postfix;
|
||||
}
|
||||
return s.replace(/[^0-9a-z ]/gi, '').toLowerCase().split(/\s+/).join('-');
|
||||
}
|
||||
|
||||
const Controls = React.createClass({
|
||||
render: function () {
|
||||
const state = this.props.p;
|
||||
return React.DOM.div(
|
||||
null,
|
||||
React.DOM.b(null, "Choose a standard:"),
|
||||
VALUATION_STANDARDS.map(function (item, index) {
|
||||
return React.DOM.label(
|
||||
{ key: index },
|
||||
React.DOM.input({
|
||||
type: 'radio',
|
||||
checked: item.get('name') === selectedMode.deref()[0],
|
||||
onChange: function (e) {
|
||||
const newValue = item.get('name');
|
||||
selectedMode.reset([newValue, newValue === 'continental' ? 'periodic' : 'perpetual']);
|
||||
}
|
||||
}),
|
||||
' ',
|
||||
item.get('text')
|
||||
);
|
||||
}),
|
||||
React.DOM.br(),
|
||||
React.DOM.b(null, "Choose an accounting method:"),
|
||||
VALUATION_METHODS.map(function (item, index) {
|
||||
return React.DOM.label(
|
||||
{ key: index },
|
||||
React.DOM.input({
|
||||
type: 'radio',
|
||||
checked: item.get('name') === selectedMode.deref()[1],
|
||||
onChange: e => selectedMode.swap(vals => [vals[0], item.get('name')]),
|
||||
}),
|
||||
' ',
|
||||
item.get('text')
|
||||
);
|
||||
}),
|
||||
React.DOM.br(),
|
||||
React.DOM.b(null, "Activate operations to see the impact:"),
|
||||
VALUATION_ENTRIES.map(function (item, key) {
|
||||
return React.DOM.label(
|
||||
{
|
||||
key: key,
|
||||
style: { display: 'block' },
|
||||
className: (key === state.get('active') ? 'highlight-op' : void 0)
|
||||
},
|
||||
React.DOM.input({
|
||||
type: 'checkbox',
|
||||
checked: state.get('operations').contains(key),
|
||||
onChange: function (e) {
|
||||
if (e.target.checked) {
|
||||
selectedOps.swap(d => d.set('active', key)
|
||||
.update('operations', ops => ops.add(key)));
|
||||
} else {
|
||||
selectedOps.swap(d => d.set('active', null)
|
||||
.update('operations', ops => ops.remove(key)));
|
||||
}
|
||||
}
|
||||
}),
|
||||
' ',
|
||||
item.get('title')
|
||||
);
|
||||
}),
|
||||
React.DOM.br(),
|
||||
"Closing",
|
||||
VALUATION_REVIEWS.map(function (item, key) {
|
||||
// We bold the text if any of the operations in this review is
|
||||
// relevant to the currently selected operations.
|
||||
const boldable = item.getIn([...selectedMode.deref(), 'operations'])
|
||||
.some(function (op) {
|
||||
if (!op.has('entries') && !op.has('except'))
|
||||
return true;
|
||||
const opset = state.get('operations').toSet();
|
||||
if (opset.isSuperset(op.get('entries', []))
|
||||
&& opset.intersect(op.get('except', [])).isEmpty())
|
||||
return true;
|
||||
});
|
||||
return React.DOM.label(
|
||||
{
|
||||
key: key,
|
||||
style: { display: 'block' },
|
||||
className: (key === state.get('active') ? 'highlight-op' : void 0)
|
||||
},
|
||||
React.DOM.input({
|
||||
type: 'checkbox',
|
||||
checked: state.get('operations').contains(key),
|
||||
onChange: function (e) {
|
||||
if (e.target.checked) {
|
||||
selectedOps.swap(d => d.set('active', key)
|
||||
.update('operations', ops => ops.add(key)));
|
||||
} else {
|
||||
selectedOps.swap(d => d.set('active', null)
|
||||
.update('operations', ops => ops.remove(key)));
|
||||
}
|
||||
}
|
||||
}),
|
||||
' ',
|
||||
boldable ? React.DOM.b(null, item.get('title')) : item.get('title'),
|
||||
);
|
||||
}),
|
||||
React.DOM.br(),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const Chart = React.createClass({
|
||||
render: function () {
|
||||
// Only used for highlighting cells.
|
||||
const lastop = Immutable.Map(
|
||||
this.props.p.get('active')
|
||||
? (VALUATION_ENTRIES.concat(VALUATION_REVIEWS)
|
||||
.getIn([this.props.p.get('active'), ...selectedMode.deref(), 'operations'], Immutable.List()))
|
||||
.map(op => [VALUATION_JOURNALS.getIn([selectedMode.deref()[0], ...op.get('account'), 'code']),
|
||||
op.has('credit') ? 'credit' : 'debit'])
|
||||
: Immutable.Map());
|
||||
return React.DOM.div(
|
||||
null,
|
||||
React.DOM.table(
|
||||
{ className: 'table table-condensed' },
|
||||
React.DOM.thead(
|
||||
null,
|
||||
React.DOM.tr(
|
||||
null,
|
||||
React.DOM.th(),
|
||||
React.DOM.th({ className: 'text-right' }, "Debit"),
|
||||
React.DOM.th({ className: 'text-right' }, "Credit"),
|
||||
React.DOM.th({ className: 'text-right' }, "Balance"))
|
||||
),
|
||||
React.DOM.tbody(
|
||||
null,
|
||||
this.accounts().map(function (data) {
|
||||
// Don't highlight the cell if it's going to be empty.
|
||||
const highlight = lastop.get(data.get('code')),
|
||||
debit = format(data.get('debit')),
|
||||
credit = format(data.get('credit'));
|
||||
return React.DOM.tr(
|
||||
{
|
||||
key: data.get('code'),
|
||||
className: data.get('level') ? 'parent-line' : 'child-line',
|
||||
},
|
||||
React.DOM.th(
|
||||
null,
|
||||
data.get('level') ? '\u2001 ' : '',
|
||||
data.get('code') || '', ' ', data.get('title')
|
||||
),
|
||||
React.DOM.td(
|
||||
{ className: React.addons.classSet({
|
||||
'text-right': true,
|
||||
'highlight-op': debit ? highlight === 'debit' : void 0 }) },
|
||||
debit),
|
||||
React.DOM.td(
|
||||
{ className: React.addons.classSet({
|
||||
'text-right': true,
|
||||
'highlight-op': credit ? highlight === 'credit' : void 0 }) },
|
||||
credit),
|
||||
React.DOM.td(
|
||||
{ className: 'text-right' },
|
||||
((data.get('debit') || data.get('credit'))
|
||||
? format(data.get('debit') - data.get('credit'), 0)
|
||||
: ''),
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
accounts: function() {
|
||||
const currentOperations = this.props.p.get('operations');
|
||||
if (!currentOperations)
|
||||
return null;
|
||||
const totals = VALUATION_ENTRIES.concat(VALUATION_REVIEWS)
|
||||
.filter((val, key) => currentOperations.includes(key))
|
||||
.valueSeq()
|
||||
.flatMap(entry => entry.getIn([...selectedMode.deref(), 'operations']))
|
||||
.reduce(function (acc, op) {
|
||||
// `entries' and `except' fields are explained in valuation-data.js (quod vide)
|
||||
if (op.has('entries') || op.has('except')) {
|
||||
const opset = currentOperations.toSet();
|
||||
if (!(opset.isSuperset(op.get('entries', []))
|
||||
&& opset.intersect(op.get('except', [])).isEmpty())) {
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
const code = VALUATION_JOURNALS.getIn([selectedMode.deref()[0], ...op.get('account'), 'code']);
|
||||
return acc
|
||||
.updateIn([code, 'debit'],
|
||||
d => (d || 0) + op.get('debit', 0))
|
||||
.updateIn([code, 'credit'],
|
||||
c => (c || 0) + op.get('credit', 0));
|
||||
}, Immutable.Map());
|
||||
return accounts.get(selectedMode.deref()[0]).map(account =>
|
||||
account.merge(account.get('accounts')
|
||||
.map(code => totals.get(code, NULL))
|
||||
.reduce((acc, it) => acc.mergeWith((a, b) => a + b, it, NULL))));
|
||||
}
|
||||
});
|
||||
|
||||
const NULL = Immutable.Map({ debit: 0, credit: 0 });
|
||||
const accounts = VALUATION_JOURNALS.map(method => method.toList().flatMap(function (cat) {
|
||||
return Immutable.Seq.of(cat.set('level', 0)).concat(cat.filter(function (v, k) {
|
||||
return k.toUpperCase() === k;
|
||||
}).toIndexedSeq().map(function (acc) { return acc.set('level', 1) }));
|
||||
}).map(function (account) { // add accounts: Seq<AccountCode> to each account
|
||||
return account.set(
|
||||
'accounts',
|
||||
Immutable.Seq.of(account.get('code')).concat(
|
||||
account.toIndexedSeq().map(function (val) {
|
||||
return Immutable.Map.isMap(val) && val.get('code');
|
||||
}).filter(function (val) { return !!val; })
|
||||
)
|
||||
);
|
||||
}));
|
||||
function format(val, def) {
|
||||
if (!val) { return def === undefined ? '' : def; }
|
||||
if (val % 1 === 0) { return val; }
|
||||
return val.toFixed(2);
|
||||
}
|
||||
})();
|
||||
1343
static/js/valuation-data.js
Normal file
176
static/js/valuation-journal.js
Normal file
@@ -0,0 +1,176 @@
|
||||
/* global Immutable, React */
|
||||
/* global createAtom, findAncestor */
|
||||
/* global VALUATION_{STANDARDS,METHODS,JOURNALS,ENTRIES,REVIEWS} */
|
||||
(function () {
|
||||
'use strict';
|
||||
// NOTE: used by valuation cheat_sheet.rst
|
||||
|
||||
const selectedMode = createAtom()
|
||||
const selectedOp = createAtom();
|
||||
|
||||
const entries = VALUATION_ENTRIES.concat(VALUATION_REVIEWS);
|
||||
|
||||
function watch (next) {
|
||||
React.render(
|
||||
React.createElement(Controls, { entryKey: next }),
|
||||
document.getElementById('journaling-entries-controls'));
|
||||
React.render(
|
||||
React.createElement(FormatEntry, { entryKey: next }),
|
||||
document.querySelector('.journal-entries'));
|
||||
}
|
||||
|
||||
selectedOp.addWatch('chart', (k, m, prev, next) => watch([next, ...selectedMode.deref()]));
|
||||
selectedMode.addWatch('chart', (k, m, prev, next) => watch([selectedOp.deref(), ...next]));
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const entriesSection = findAncestor(document.querySelector('.journal-entries'), 'section');
|
||||
if (!entriesSection) { return; }
|
||||
|
||||
const controls = document.createElement('div');
|
||||
controls.setAttribute('id', 'journaling-entries-controls');
|
||||
entriesSection.insertBefore(controls, entriesSection.lastElementChild);
|
||||
|
||||
selectedMode.reset(['continental', 'periodic']);
|
||||
selectedOp.reset('initial_inventory');
|
||||
});
|
||||
|
||||
const Controls = React.createClass({
|
||||
render: function () {
|
||||
const key = this.props.entryKey;
|
||||
return React.DOM.div(
|
||||
null,
|
||||
React.DOM.b(null, "Choose a standard:"),
|
||||
VALUATION_STANDARDS.map(function (item, index) {
|
||||
return React.DOM.label(
|
||||
{ key: index },
|
||||
React.DOM.input({
|
||||
type: 'radio',
|
||||
checked: item.get('name') === key[1],
|
||||
onChange: function (e) {
|
||||
const newValue = item.get('name');
|
||||
selectedMode.reset([newValue, newValue === 'continental' ? 'periodic' : 'perpetual']);
|
||||
}
|
||||
}),
|
||||
' ',
|
||||
item.get('text')
|
||||
);
|
||||
}),
|
||||
React.DOM.br(),
|
||||
React.DOM.b(null, "Choose an accounting method:"),
|
||||
VALUATION_METHODS.map(function (item, index) {
|
||||
return React.DOM.label(
|
||||
{ key: index },
|
||||
React.DOM.input({
|
||||
type: 'radio',
|
||||
checked: item.get('name') === key[2],
|
||||
onChange: e => selectedMode.swap(vals => [vals[0], item.get('name')]),
|
||||
}),
|
||||
' ',
|
||||
item.get('text')
|
||||
);
|
||||
}),
|
||||
React.DOM.br(),
|
||||
React.DOM.b(null, "Activate operations to see the impact:"),
|
||||
VALUATION_ENTRIES.map(function (item, index) {
|
||||
return React.DOM.label(
|
||||
{ key: index },
|
||||
React.DOM.input({
|
||||
type: 'radio',
|
||||
checked: index === key[0],
|
||||
onChange: e => selectedOp.reset(index),
|
||||
}),
|
||||
' ',
|
||||
item.get('title')
|
||||
);
|
||||
}),
|
||||
React.DOM.br(),
|
||||
"Closing",
|
||||
VALUATION_REVIEWS.map(function (item, index) {
|
||||
return React.DOM.label(
|
||||
{ key: index },
|
||||
React.DOM.input({
|
||||
type: 'radio',
|
||||
checked: index === key[0],
|
||||
onChange: e => selectedOp.reset(index),
|
||||
}),
|
||||
' ',
|
||||
item.get('title')
|
||||
);
|
||||
}),
|
||||
React.DOM.br(),
|
||||
);
|
||||
}
|
||||
});
|
||||
const FormatEntry = React.createClass({
|
||||
render: function () {
|
||||
const entry = entries.getIn(this.props.entryKey);
|
||||
return React.DOM.div(
|
||||
null,
|
||||
React.DOM.table(
|
||||
{ className: 'table table-sm d-c-table' },
|
||||
React.DOM.thead(
|
||||
null,
|
||||
React.DOM.tr(
|
||||
null,
|
||||
React.DOM.th(),
|
||||
React.DOM.th(null, "Debit"),
|
||||
React.DOM.th(null, "Credit"),
|
||||
)
|
||||
),
|
||||
React.DOM.tbody(
|
||||
null,
|
||||
// Use `journal_operations' if it's a review. See `valuation-data.js'.
|
||||
entry && entry.get('journal_operations', entry.get('operations', [])).map(this.renderRow)
|
||||
)
|
||||
),
|
||||
React.createElement(Listing, {
|
||||
heading: "Explanation",
|
||||
items: entry && entry.get('explanation'),
|
||||
}),
|
||||
React.createElement(Listing, {
|
||||
heading: "Configuration",
|
||||
items: entry && entry.get('configuration'),
|
||||
})
|
||||
);
|
||||
},
|
||||
renderRow: function (entry, index) {
|
||||
const standard = this.props.entryKey[1];
|
||||
if (!entry) {
|
||||
return React.DOM.tr(
|
||||
{ key: 'spacer-' + index },
|
||||
React.DOM.td({ colSpan: 3 }, "\u00A0")
|
||||
);
|
||||
}
|
||||
const journalEntry = VALUATION_JOURNALS.getIn([standard, ...entry.get('account')]);
|
||||
const title = journalEntry.get('title');
|
||||
// Don't display 0 for 'General Balance for Inventory Initial Value'
|
||||
const code = journalEntry.get('code') || '';
|
||||
return React.DOM.tr(
|
||||
{ key: index },
|
||||
React.DOM.td(null, `${code} ${title}`),
|
||||
React.DOM.td(null, entry.get('debit')),
|
||||
React.DOM.td(null, entry.get('credit'))
|
||||
);
|
||||
}
|
||||
});
|
||||
const Listing = React.createClass({
|
||||
render: function () {
|
||||
if (!this.props.items || this.props.items.isEmpty()) {
|
||||
return React.DOM.div();
|
||||
}
|
||||
const items = this.props.items;
|
||||
const idx = items.indexOf(null);
|
||||
if (idx !== -1) {
|
||||
// console.log(items.slice(idx + 1).deref());
|
||||
items = items.take(idx);
|
||||
}
|
||||
return React.DOM.div(
|
||||
{ className: 'entries-listing' },
|
||||
React.DOM.h4(null, this.props.heading, ':'),
|
||||
items.map(function (item, index) {
|
||||
return React.DOM.p({ key: index }, item);
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}());
|
||||