Import Create Missing
On this page
Adds a Create New Document if Not Found checkbox to a base.import.pdf.template.line, and a New Document Values table that appears once
it's checked. When this line's search (Search field / Search subfield) misses, instead
of leaving the line unresolved (the base module's own behaviour: fall back to Fixed value/default_value, or the raw extracted text), the missing document is
created and linked - filled in from the New Document Values table.
Why
A private, supplier-specific lookup module already does this for one specific case
(creating product.product records from a supplier part number via a third-party lookup
API). This module generalizes the same idea to any linked model, filled from the
template's own extracted data instead of an external API: a Fixed value, or a Variable
value pulled out with the same Pattern regex (and, if import_create_missing_xberg is
also installed, the same JSONPath narrowing) the line itself uses.
How it works
New Document Values is a list of base.import.pdf.template.line.create.value rows -
click a row (or Add a line) to open it, the same list-opens-a-form pattern the
template's own Lines tab uses for base.import.pdf.template.line. It's not an
inline editable table: several of a row's fields are mutually exclusive depending on its Field/Type (a fixed value vs. a record picker vs. a pattern vs. a nested table),
and a form can genuinely hide a field it doesn't need - an inline table's columns are
always there whether or not that row uses them, which is confusing once there's more
than one kind of value. The compact list shows a one-line Value summary instead.
Each row sets one field on the document to create:
Field: any field on the line's linked model (
Field's relation) - including a field inherited from a delegated parent model (e.g.product.product's own fields as well as everything it delegates fromproduct.template).Type:
Fixed,VariableorOdoo Default- not shown for a one2many field (see below).Value (Fixed only): a typed input chosen from the field's own type - a checkbox for a boolean, a number for integer/float/monetary, a dropdown for a selection, a date picker for a date, a record picker for a many2one/reference, and plain text for text/html/json.
Pattern (Variable only): a regex, evaluated the same way the line's own
Patternis - once per row of alinestable (or once for aheaderline), in the same position/alignment as the line's own extracted values. For example, if the line's ownPatternfinds a part number in every row of a table, a New Document Values row withPatternset to match that table's Description column fills innamefor the row at the same position.Odoo Default rows carry no value at all: the field is deliberately left out of the
create()call so Odoo applies its own default. Used automatically for a required field whose default is computed at creation time (a date/datetime that means "now"), where freezing a value into the template would be wrong.
The row a New Document Value's Pattern matched is paired to the line's own row by
position: the 3rd match of a New Document Values row's pattern goes with the 3rd match
of the line's own pattern. If a New Document Values row's pattern produces a different
number of matches than the line itself does (a ragged extraction - usually a sign the
pattern is wrong), no document is created for any row of that line and a warning is
logged; positions are never silently paired incorrectly to avoid, say, naming one
product after a different row's description.
The search field (if it's a plain field directly on the linked model, not a one2many/relation) is pre-filled with the value that was searched for and missed, so
importing the same source again finds the just-created record instead of creating a
duplicate - a New Document Values row for the same field overrides this if present.
Document creation only ever happens during a real import - wizard.base.import.pdf.upload.line._process_form() (used by both a manual upload and import_from_email's EDI intake). A template's live preview (import_preview) or the
standalone preview wizard never creates anything, even with Create New Document if Not Found checked - there is nothing to link the created record
to.
Required fields are filled in for you
Checking Create New Document if Not Found pre-populates the table with every field
the document genuinely requires - create() would reject the record without them - so
there is nothing to look up by hand. The same happens when a one2many row's Row
Values table is first opened: it fills with that sub-model's own required fields. Add Required Fields on an already-saved line does it on demand, for a line
configured before this existed or whose target model has since gained a required field.
What lands in each row depends on whether Odoo itself has a default:
No default (e.g. a product's
name): an emptyVariablerow. You still have to say where the value comes from - that is the point of surfacing it.A default (e.g. a product's Product Type, Product Category, Unit of Measure): a
Fixedrow pre-set to that Odoo default, so the default is visible and editable instead of invisible.
The search field is never added, because it is already pre-filled with the value that
missed (see below). Neither is a delegation link (product.product.product_tmpl_id), a
computed field, or a one2many's own inverse - all three are set by the very same create().
Required-field discovery follows delegated (_inherits) parents, so creating a product.product correctly asks for product.template's fields too. Adding your own
rows works exactly as before, and auto-population never touches, reorders or removes a
row you added or edited - it only ever appends what is missing. Rows pointing at a model
that is no longer the target (because Field was re-pointed) are dropped.
The missing-values warning
A row whose required field still has no value is highlighted in the list and marked Missing, and the tab shows a banner naming every one of them - nested ones included,
as Bank Accounts › Account Number. Saving the line while some are still unfilled shows
a warning notification.
The warning is advisory only: the line saves, the dialog closes, and you can come back later. Nothing is blocked, because a template is usually built over several sittings and against a document you may not have in front of you. A required field with an Odoo default is never reported - Odoo fills it in.
Nested one2many fields
If Field is a one2many (e.g. a newly-created vendor's own bank_ids, or a product's seller_ids), the row's Type/Value/Pattern disappear and a Row Values table takes
their place - the exact same list-opens-a-form widget, one row per field of the
one2many's own model (e.g. a bank account's acc_number), each with its own
Type/Value/Pattern. Fill it in one field at a time, the same way you would the outer
table. Row Values live only inside their one2many row: open Vendors to see its Vendor and Vendor Product Code - they are not listed in the outer table.
A product line always gets a Vendors (seller_ids) row, with its required Vendor inside, even though nothing makes seller_ids required: a product created
without a vendor row can never be found again by vendor code, so every later import
would create a duplicate.
How Row Values are matched depends on the line:
linesline: one sub-record per row, from that row's own values. Each Variable Row Value is extracted as a column aligned to the line's own table rows, exactly like any other New Document Values column - so on a two-item order, product A's vendor row gets A's vendor code and product B's gets B's. Fixed Row Values apply to every row's sub-record. A Row Values column that doesn't line up with the line's rows skips document creation for the whole line, same as an outer column does.headerline: matched across the whole document. With only one row to begin with, Row Values search the whole document and are paired by match position - the 1stacc_numbermatch goes with the 1st bank account, and so on - so one header can create several sub-records (the case this was built for: an email describing one vendor with several bank accounts). AFixed-only one2many creates exactly one sub-record, and a mismatched match count between two Variable Row Values skips the whole one2many rather than pairing rows incorrectly.
A one2many nested inside another one2many has no table row of its own, so it always uses the whole-document behaviour.
When a document is not created
Every reason a document was not created is written to Settings → Technical → Logging (filter Path = import_create_missing), on its own database cursor. That matters: a
failed creation usually fails the whole import, which rolls the import's own transaction
back - a log entry written there would vanish with it. You will find either:
failed to create a missing … record for '…': <reason>- Odoo rejected thecreate(), and the reason is Odoo's own error; or… extracted N value(s) but the line itself has M row(s) - skipping document creation- a New Document Values column (named, with its parent for a Row Value, e.g.Vendors › Vendor Product Code) didn't line up with the line's own rows.
On an Xberg template, the warning banner also names every Variable row whose Pattern has no JSONPath of its own on a line that uses one: that Pattern searches the whole JSON document instead of the line's cells, and usually comes up empty.
Configuration
On a template line with a many2one
Fieldand aSearch fieldset, check Create New Document if Not Found. New Document Values fills itself with the document's required fields.Fill in the rows that came up empty (the ones Odoo has no default for), and adjust any pre-set default you don't want. Add rows of your own for anything else the new document should carry.
Import a document containing a value the search won't find.
Tests
python odoo/odoo-bin -c odoo.conf -d <testdb> -i import_create_missing \
--test-enable --test-tags /import_create_missing --stop-after-init