ferrosoft.apps.emiflow package

Emiflow — the emission calculation and reporting engine of the platform.

Emiflow turns business activity — freight movements, energy use, waste treatment — into auditable greenhouse-gas figures (gCO2e per tkm for transports, gCO2e per t for treatments), persists those figures in an append-only booking journal, and renders them as compliance-ready documents. It is the application that justifies the platform’s existence: emissions are not estimated here, they are accounted for.

Domain overview

The emiflow domain is built around four interlocking concepts:

  • Transport chains. A TransportChain is one consignment from consignor to consignee, broken into ordered TransportChainElement (TCE) hops. The consignment’s payload is described once at the chain level as one or more FreightItem rows — quantified material allocated to the chain and carried unchanged through every TCE. Each TCE then references a TransportOperation, the activity definition that pairs an Emitter (vehicle, machine) with an EnergyCarrier (fuel) and an activity type, so the same operation can be reused across many chains.

  • Treatments. A Treatment is a recipe: which materials go in, which come out, which emitter and energy carrier are involved, and how the resulting emissions are allocated to the outputs (by weight or manually). A TreatmentOperation is an instance of that recipe with concrete input and output lines.

  • Emission intensities. An EmissionIntensity is the calculated per-activity factor (gCO2e per tkm or gCO2e per t) for a transport operation, treatment operation, or emitter. The intensity carries every input variable it was computed from, so results stay reproducible and audit-traceable.

  • Booking records. An EmissionBookingRecord is an immutable journal entry of calculated emissions, broken down by LifecycleCategory (LCC). The LCC split lets reports present well-to-tank, tank-to-wheel, and well-to-wheel figures side by side without recomputation.

Upstream reference data is published as EmissionsCatalog bundles (for example the public GLEC extract) and per-tenant catalogs can override or extend the shipped factors. Per-tenant configuration lives in a singleton Settings row and the related TransportChainDefaults and BusinessCentralSettings records.

Package layout

Emiflow follows the platform-wide fat services / thin views convention. The major subpackages are:

  • models — ORM models grouped one module per bounded sub-domain (booking, catalog, dataimport, intensity, operation, reporting, settings, signals, tegos, transport, treatment).

  • services — business logic: transport-chain and treatment emission calculators, fluent TransportChain builders, product carbon footprint (PCF) roll-ups, document generators for reports, per-tenant tonnage accounting for subscription billing, vehicle heuristics, and visitor implementations driven by TransportChain.accept.

  • views — Django class-based views exposing the per-domain CRUD bundles and ad-hoc workflows (file import, treatment-operation allocation, reporting, settings).

  • forms, tables, filter, widgets — Django forms, django-tables2 tables, django-filter FilterSets, and custom widgets that surface the models in the web UI.

  • api — DRF router and viewsets mounted under api/ for machine consumers.

  • dataimport — staging-to-final pipeline that turns rows from bulk spreadsheet imports into real transport chains.

  • catalog — ingest utilities for third-party emission catalog files (distinct from catalog, which holds the resulting ORM models).

  • templatetags — Jinja2 template tags used by the emiflow UI templates.

  • management — Django management commands.

Top-level modules cover cross-cutting concerns:

  • apps — Django AppConfig that contributes the Emissions settings-menu category (Emitters, Transport Defaults, Lifecycle Categories, Energy Carriers) and hooks the emiflow updater into the ferrobase_update management command.

  • urls — URL configuration under the emiflow namespace, registering the dashboard, the per-domain CRUD bundles, the ad-hoc workflow endpoints, and the REST API.

  • metrics — Prometheus metrics for per-tenant tonnage usage and per-organisation tonnage limits, used by subscription billing and overage alerting.

External integrations

Emiflow integrates with Microsoft Dynamics 365 Business Central through two distinct apps hosted on that platform:

  • Built-in emissions app. The connector in businesscentral targets the emissions module that ships with Business Central itself, syncing master data with the Microsoft-provided app (through RAPIDSTART files). Per-tenant connection details live in BusinessCentralSettings.

  • TEGOS app. TEGOS is a waste-management extension app for Business Central. Order documents originating in TEGOS are parsed into the in-memory value objects defined in tegos and converted into transport chains and freight items.

Actually, the apps are not directly targeted, but require Ferrosoft apps acting as an adapter for these apps:

  • bc-emiflow (needed to consume the RAPIDSTART file)

  • bc-emiflow-for-tegos (providing TEGOS-specific stuff atop bc-emiflow)

Subpackages

Submodules

ferrosoft.apps.emiflow.admin module

ferrosoft.apps.emiflow.apps module

Django application configuration for the emiflow app.

Wires up settings-menu entries (under the Emissions category) and hooks emiflow updater into ferrobase_update management command.

class ferrosoft.apps.emiflow.apps.EmiflowConfig(app_name, app_module)[source]

Bases: AppConfig

Django AppConfig for the emiflow application.

Registers the emiflow settings menu items with the shared SettingsMenuRegistry and hooks emiflow updater into ferrobase_update management command.

SETTINGS_MENU = [MenuItem(display_name='Emitters', view_name='emiflow:emitter_index', permissions=['ferrobase.view_material'], category_name='Emissions', icon=None), MenuItem(display_name='Transport Defaults', view_name='emiflow:settings_transport_defaults', permissions=['emiflow.view_transportchaindefaults'], category_name='Emissions', icon=None), MenuItem(display_name='Lifecycle Categories', view_name='emiflow:lifecyclecategory_index', permissions=['emiflow.view_lifecyclecategory'], category_name='Emissions', icon=None), MenuItem(display_name='Energy Carriers', view_name='emiflow:energycarrier_index', permissions=['emiflow.view_energycarrier'], category_name='Emissions', icon=None)]

Menu items contributed to the Emissions category of the settings navigation. Each item is gated by the listed model permissions.

default_auto_field = 'django.db.models.BigAutoField'
name = 'ferrosoft.apps.emiflow'
ready()[source]

Hook the emiflow updater into ferrobase_update and contribute settings menu entries.

verbose_name = 'Emiflow'

ferrosoft.apps.emiflow.filter module

django-filter FilterSet definitions for emiflow list views.

Each FilterSet here backs a corresponding list view: it parses the request query string, validates it against the model fields declared in Meta, and produces a filtered queryset plus a bound form that templates render as the filter sidebar. Custom method filters support filtering by related-object primary key, by membership in a PostgreSQL array column, and by IN clauses for multi-select widgets.

class ferrosoft.apps.emiflow.filter.BusinessPartnerFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter business partners by name, street, city, or postal code prefix.

class Meta[source]

Bases: object

fields = {'city': ['icontains'], 'name': ['icontains'], 'postal_code': ['startswith'], 'street': ['icontains']}
model

alias of BusinessPartner

base_filters = {'city__icontains': <django_filters.filters.CharFilter object>, 'name__icontains': <django_filters.filters.CharFilter object>, 'postal_code__startswith': <django_filters.filters.CharFilter object>, 'street__icontains': <django_filters.filters.CharFilter object>}
declared_filters = {}
class ferrosoft.apps.emiflow.filter.EmissionBookingRecordFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter emission booking records by lifecycle category, booking-date range, material, and partners.

Booking records carry materials in a PostgreSQL array column, so material_id uses _filter_by_pk_array() rather than a normal foreign-key lookup. Consignor and consignee filters exclude partners with an empty name.

class Meta[source]

Bases: object

fields = {'entity_reference': ['startswith']}
model

alias of EmissionBookingRecord

async classmethod aget_user_filtered(user_filter, request=None)[source]

Async wrapper around get_user_filtered() for ASGI views.

base_filters = {'booking_date_from': <django_filters.filters.DateFilter object>, 'booking_date_until': <django_filters.filters.DateFilter object>, 'entity_reference__startswith': <django_filters.filters.CharFilter object>, 'incoming_partner_id': <django_filters.filters.ModelChoiceFilter object>, 'lifecycle_category': <django_filters.filters.ModelMultipleChoiceFilter object>, 'material_id': <django_filters.filters.ModelChoiceFilter object>, 'outgoing_partner_id': <django_filters.filters.ModelChoiceFilter object>}
declared_filters = {'booking_date_from': <django_filters.filters.DateFilter object>, 'booking_date_until': <django_filters.filters.DateFilter object>, 'incoming_partner_id': <django_filters.filters.ModelChoiceFilter object>, 'lifecycle_category': <django_filters.filters.ModelMultipleChoiceFilter object>, 'material_id': <django_filters.filters.ModelChoiceFilter object>, 'outgoing_partner_id': <django_filters.filters.ModelChoiceFilter object>}
classmethod get_user_filtered(user_filter, request=None)[source]

Apply a persisted report filter to EmissionBookingRecord.

Reports persist the filter query string the user originally configured. This method re-applies that query string to produce the same record set later. An empty user_filter returns no records, forcing the user to select filters before a report is generated.

Parameters:
  • user_filter (str) – The persisted query string (request.GET.urlencode()) captured when the report was created.

  • request (HttpRequest | None) – Current request; when it carries a sort GET param, that ordering is applied. Defaults to descending booking date.

Returns:

The filtered, ordered queryset and the bound filter instance.

Return type:

tuple[QuerySet, EmissionBookingRecordFilter]

class ferrosoft.apps.emiflow.filter.EmissionFactorFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter emission factors by energy carrier name substring.

class Meta[source]

Bases: object

fields = {'energy_carrier__name': ['icontains']}
model

alias of EmissionFactor

base_filters = {'energy_carrier__name__icontains': <django_filters.filters.CharFilter object>}
declared_filters = {}
class ferrosoft.apps.emiflow.filter.EmitterFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter emitters by name substring, catalog, type, vehicle type, or energy carrier.

class Meta[source]

Bases: object

fields = {'catalog': ['exact'], 'energy_carrier': ['exact'], 'epitype': ['exact'], 'name': ['icontains'], 'vehicle_type': ['exact']}
model

alias of Emitter

base_filters = {'catalog': <django_filters.filters.ModelChoiceFilter object>, 'energy_carrier': <django_filters.filters.ModelChoiceFilter object>, 'epitype': <django_filters.filters.ChoiceFilter object>, 'name__icontains': <django_filters.filters.CharFilter object>, 'vehicle_type': <django_filters.filters.ChoiceFilter object>}
declared_filters = {}
class ferrosoft.apps.emiflow.filter.EnergyCarrierFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter energy carriers by name substring or catalog.

class Meta[source]

Bases: object

fields = {'catalog': ['exact'], 'name': ['icontains']}
model

alias of EnergyCarrier

base_filters = {'catalog': <django_filters.filters.ModelChoiceFilter object>, 'name__icontains': <django_filters.filters.CharFilter object>}
declared_filters = {}
class ferrosoft.apps.emiflow.filter.ReportFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter reports by reference prefix, name substring, type, or creation-time range.

class Meta[source]

Bases: object

fields = {'name': ['icontains'], 'reference': ['startswith'], 'type': ['exact']}
model

alias of Report

base_filters = {'creation_time_from': <django_filters.filters.DateTimeFilter object>, 'creation_time_until': <django_filters.filters.DateTimeFilter object>, 'name__icontains': <django_filters.filters.CharFilter object>, 'reference__startswith': <django_filters.filters.CharFilter object>, 'type': <django_filters.filters.ChoiceFilter object>}
declared_filters = {'creation_time_from': <django_filters.filters.DateTimeFilter object>, 'creation_time_until': <django_filters.filters.DateTimeFilter object>}
class ferrosoft.apps.emiflow.filter.TransportChainFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter transport chains by type, reference, booking-date range, incoterms, and simulation flag.

class Meta[source]

Bases: object

fields = []
model

alias of TransportChain

base_filters = {'booking_date_from': <django_filters.filters.DateFilter object>, 'booking_date_until': <django_filters.filters.DateFilter object>, 'incoterms': <django_filters.filters.ChoiceFilter object>, 'reference_like': <django_filters.filters.CharFilter object>, 'simulation': <django_filters.filters.BooleanFilter object>, 'transport_type': <django_filters.filters.ChoiceFilter object>}
declared_filters = {'booking_date_from': <django_filters.filters.DateFilter object>, 'booking_date_until': <django_filters.filters.DateFilter object>, 'incoterms': <django_filters.filters.ChoiceFilter object>, 'reference_like': <django_filters.filters.CharFilter object>, 'simulation': <django_filters.filters.BooleanFilter object>, 'transport_type': <django_filters.filters.ChoiceFilter object>}
class ferrosoft.apps.emiflow.filter.TransportOperationFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter transport operations by reference, name, activity type, emitter, or energy carrier.

class Meta[source]

Bases: object

fields = {'activity_type': ['exact'], 'emitter': ['exact'], 'energy_carrier': ['exact'], 'name': ['icontains'], 'reference': ['startswith']}
model

alias of TransportOperation

base_filters = {'activity_type': <django_filters.filters.ChoiceFilter object>, 'emitter': <django_filters.filters.ModelChoiceFilter object>, 'energy_carrier': <django_filters.filters.ModelChoiceFilter object>, 'name__icontains': <django_filters.filters.CharFilter object>, 'reference__startswith': <django_filters.filters.CharFilter object>}
declared_filters = {}
class ferrosoft.apps.emiflow.filter.TreatmentFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter treatments by reference, related operation, related emitter/energy carrier, or booking-date range.

class Meta[source]

Bases: object

fields = {'operation__emitter__name': ['icontains'], 'operation__energy_carrier__name': ['icontains'], 'reference': ['startswith']}
model

alias of Treatment

base_filters = {'booking_date_from': <django_filters.filters.DateFilter object>, 'booking_date_until': <django_filters.filters.DateFilter object>, 'operation': <django_filters.filters.ModelChoiceFilter object>, 'operation__emitter__name__icontains': <django_filters.filters.CharFilter object>, 'operation__energy_carrier__name__icontains': <django_filters.filters.CharFilter object>, 'reference__startswith': <django_filters.filters.CharFilter object>}
declared_filters = {'booking_date_from': <django_filters.filters.DateFilter object>, 'booking_date_until': <django_filters.filters.DateFilter object>, 'operation': <django_filters.filters.ModelChoiceFilter object>}
class ferrosoft.apps.emiflow.filter.TreatmentOperationFilter(data=None, queryset=None, *, request=None, prefix=None)[source]

Bases: FilterSet

Filter treatment operations by reference, name, status, emitter, or energy carrier.

class Meta[source]

Bases: object

fields = {'name': ['icontains'], 'reference': ['startswith'], 'status': ['exact']}
model

alias of TreatmentOperation

base_filters = {'emitter': <django_filters.filters.ModelChoiceFilter object>, 'energy_carrier': <django_filters.filters.ModelChoiceFilter object>, 'name__icontains': <django_filters.filters.CharFilter object>, 'reference__startswith': <django_filters.filters.CharFilter object>, 'status': <django_filters.filters.ChoiceFilter object>}
declared_filters = {'emitter': <django_filters.filters.ModelChoiceFilter object>, 'energy_carrier': <django_filters.filters.ModelChoiceFilter object>}

ferrosoft.apps.emiflow.metrics module

Prometheus metrics for emiflow tonnage tracking.

Exposes per-tenant tonnage usage and per-organisation tonnage limits so subscription overages can be alerted on and dashboarded.

ferrosoft.apps.emiflow.metrics.TonnageCounter = prometheus_client.metrics.Counter(emiflow_tonnage)

Cumulative tonnage processed per (tenant, organization) pair, in metric tons. Seeded at startup from the database and incremented as new booking records are committed.

ferrosoft.apps.emiflow.metrics.TonnageLimit = prometheus_client.metrics.Gauge(emiflow_tonnage_limit)

Per-organisation maximum tonnage allowed by the active subscription plan, in metric tons. Zero when no plan is attached.

ferrosoft.apps.emiflow.metrics.setup()[source]

Seed metrics from the current database state.

Initialises TonnageCounter from the tonnage service’s summary so the counter does not start at zero after a process restart, and publishes TonnageLimit for every organisation that has a subscription plan attached.

ferrosoft.apps.emiflow.tables module

django-tables2 table classes for emiflow list and detail views.

Each table maps a model (or a flat record produced by an aggregation) to the columns shown in templates, with column types drawn from ferrosoft.apps.ferrobase.tables for shared rendering behaviour (number formatting, action buttons, linkified cells). Tables ending in CRUDTable add an actions column with edit/delete buttons whose delete URL each subclass resolves in get_delete_action_url.

class ferrosoft.apps.emiflow.tables.AllocationEditorTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

Inline-editable allocation table for treatment-operation output lines.

Renders each output material’s weight alongside a NumberInput so operators can adjust the percentage allocated to that material; inputs are tagged with data-material-id to be picked up by the client-side validation script that enforces summation to 100 %.

class Meta[source]

Bases: object

fields = ['material', 'weight', 'allocated_fraction']
model

alias of TreatmentOperationOutputLine

orderable = False
base_columns = {'allocated_fraction': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'material': <django_tables2.columns.base.Column object>, 'weight': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
class ferrosoft.apps.emiflow.tables.EmissionBookingRecordTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

List view table for EmissionBookingRecord showing emission, weight, and partner columns.

class Meta[source]

Bases: object

fields = ['creation_time', 'booking_date', 'lifecycle_category_name', 'entity_reference', 'emission_total_value', 'emission_operation_value', 'emission_unit_symbol', 'weight_value', 'weight_unit_symbol', 'material_code', 'material_name', 'incoming_partner_name', 'outgoing_partner_name']
model

alias of EmissionBookingRecord

base_columns = {'booking_date': <django_tables2.columns.datecolumn.DateColumn object>, 'creation_time': <django_tables2.columns.datetimecolumn.DateTimeColumn object>, 'emission_operation_value': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'emission_total_value': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'emission_unit_symbol': <django_tables2.columns.base.Column object>, 'entity_reference': <django_tables2.columns.base.Column object>, 'incoming_partner_name': <django_tables2.columns.base.Column object>, 'lifecycle_category_name': <django_tables2.columns.base.Column object>, 'material_code': <ferrosoft.apps.ferrobase.tables.columns.ArrayColumn object>, 'material_name': <ferrosoft.apps.ferrobase.tables.columns.ArrayColumn object>, 'outgoing_partner_name': <django_tables2.columns.base.Column object>, 'weight_unit_symbol': <django_tables2.columns.base.Column object>, 'weight_value': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
class ferrosoft.apps.emiflow.tables.EmissionFactorTable(*args, **kwargs)[source]

Bases: CRUDTable

CRUD table listing emission factors with category, value, and unit columns.

class Meta[source]

Bases: Meta

fields = ['actions', 'category', 'value', 'unit']
model

alias of EmissionFactor

base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'category': <django_tables2.columns.base.Column object>, 'unit': <django_tables2.columns.base.Column object>, 'value': <django_tables2.columns.base.Column object>}
get_delete_action_url(*args, **kwargs)[source]

Return the URL of the delete view for the row’s emission factor.

Return type:

str | None

class ferrosoft.apps.emiflow.tables.EmissionGradeColumn(*args, **kwargs)[source]

Bases: Column

Render a letter-grade emission badge from an emission-intensity value.

The underlying grade is a Python @property rather than a real database column, so this column is forced to be non-orderable.

render(value)[source]

Delegate to the emission_grade template tag for the badge HTML.

class ferrosoft.apps.emiflow.tables.EmitterTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

List table for Emitter with a linkified name and emission-grade badge.

class Meta[source]

Bases: object

fields = ['name', 'epitype', 'vehicle_type', 'energy_carrier', 'catalog', 'emission_intensity__grade']
model

alias of Emitter

base_columns = {'catalog': <django_tables2.columns.base.Column object>, 'emission_intensity__grade': <ferrosoft.apps.emiflow.tables.EmissionGradeColumn object>, 'energy_carrier': <django_tables2.columns.base.Column object>, 'epitype': <django_tables2.columns.base.Column object>, 'name': <django_tables2.columns.base.Column object>, 'vehicle_type': <django_tables2.columns.base.Column object>}
class ferrosoft.apps.emiflow.tables.EnergyCarrierTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

List table for EnergyCarrier showing composition, heating value, and density.

class Meta[source]

Bases: object

fields = ['name', 'composition', 'application', 'lower_heating', 'density', 'catalog']
model

alias of EnergyCarrier

base_columns = {'application': <django_tables2.columns.base.Column object>, 'catalog': <django_tables2.columns.base.Column object>, 'composition': <django_tables2.columns.base.Column object>, 'density': <django_tables2.columns.base.Column object>, 'lower_heating': <django_tables2.columns.base.Column object>, 'name': <django_tables2.columns.base.Column object>}
class ferrosoft.apps.emiflow.tables.FreightItemTable(*args, **kwargs)[source]

Bases: CRUDTable

CRUD table listing freight items with material, weight, and unit columns.

The material link is dropped before render when the current user lacks update permission on Material, so unprivileged users see a plain label rather than a non-functional link.

class Meta[source]

Bases: Meta

fields = ['actions', 'material', 'weight_value', 'weight_unit']
model

alias of FreightItem

base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'empty': <ferrosoft.apps.ferrobase.tables.columns.EmptyExpandColumn object>, 'material': <django_tables2.columns.base.Column object>, 'weight_unit': <django_tables2.columns.base.Column object>, 'weight_value': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
before_render(request)[source]

Strip the material link when the user cannot update materials.

get_delete_action_url(*args, **kwargs)[source]

Return the URL of the delete view for the row’s freight item.

Return type:

str | None

class ferrosoft.apps.emiflow.tables.LCCValueTable(*args, **kwargs)[source]

Bases: CRUDTable

Base CRUD table for lifecycle-category contribution values.

Subclassed by TransportLCCValueTable and TreatmentLCCValueTable; each subclass swaps the model and delete-URL resolution while keeping the column layout.

class Meta[source]

Bases: Meta

fields = ['actions', 'lifecycle_category', 'operation_emission', 'total_emission']
model

alias of LCCValue

orderable = False
base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'empty': <ferrosoft.apps.ferrobase.tables.columns.EmptyExpandColumn object>, 'lifecycle_category': <django_tables2.columns.base.Column object>, 'operation_emission': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'total_emission': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
class ferrosoft.apps.emiflow.tables.MultiplyProportion(fixed_width=None, mixins=None, *args, **kwargs)[source]

Bases: Column

Render-mixin that converts a 0..1 proportion into a two-decimal percentage.

render(value, *args, **kwargs)[source]

Multiply value by 100 and quantize to two decimal places.

class ferrosoft.apps.emiflow.tables.ReportTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

List table for Report with a linkified reference column.

class Meta[source]

Bases: object

fields = ['reference', 'name', 'type', 'proportional_emissions', 'creation_time']
model

alias of Report

base_columns = {'creation_time': <django_tables2.columns.datetimecolumn.DateTimeColumn object>, 'name': <django_tables2.columns.base.Column object>, 'proportional_emissions': <django_tables2.columns.base.Column object>, 'reference': <django_tables2.columns.base.Column object>, 'type': <django_tables2.columns.base.Column object>}
class ferrosoft.apps.emiflow.tables.TransportChainElementActionTable(*args, **kwargs)[source]

Bases: CRUDTable

CRUD variant of TransportChainElementTable with row delete actions.

class Meta[source]

Bases: Meta

fields = ['actions', 'position', 'epitype', 'operation', 'operation__emission_intensity__grade', 'operation__emitter', 'operation__energy_carrier', 'activity_distance_value', 'activity_distance_unit', 'activity_distance_type']
model

alias of TransportChainElement

orderable = False
base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'activity_distance_type': <django_tables2.columns.base.Column object>, 'activity_distance_unit': <django_tables2.columns.base.Column object>, 'activity_distance_value': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'epitype': <django_tables2.columns.base.Column object>, 'operation': <django_tables2.columns.base.Column object>, 'operation__emission_intensity__grade': <ferrosoft.apps.emiflow.tables.EmissionGradeColumn object>, 'operation__emitter': <django_tables2.columns.base.Column object>, 'operation__energy_carrier': <django_tables2.columns.base.Column object>, 'position': <django_tables2.columns.base.Column object>}
get_delete_action_url(*args, **kwargs)[source]

Return the delete URL for the row’s transport-chain element.

Return type:

str | None

class ferrosoft.apps.emiflow.tables.TransportChainElementTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

Read-only table listing the ordered elements of a transport chain.

class Meta[source]

Bases: object

fields = ['position', 'epitype', 'operation', 'operation__emission_intensity__grade', 'operation__emitter', 'operation__energy_carrier', 'activity_distance_value', 'activity_distance_unit', 'activity_distance_type']
model

alias of TransportChainElement

base_columns = {'activity_distance_type': <django_tables2.columns.base.Column object>, 'activity_distance_unit': <django_tables2.columns.base.Column object>, 'activity_distance_value': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'epitype': <django_tables2.columns.base.Column object>, 'operation': <django_tables2.columns.base.Column object>, 'operation__emission_intensity__grade': <ferrosoft.apps.emiflow.tables.EmissionGradeColumn object>, 'operation__emitter': <django_tables2.columns.base.Column object>, 'operation__energy_carrier': <django_tables2.columns.base.Column object>, 'position': <django_tables2.columns.base.Column object>}
class ferrosoft.apps.emiflow.tables.TransportChainTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

List table for TransportChain with linkified reference, consignor, and consignee.

Consignor/consignee links are stripped before render when the current user lacks update permission on BusinessPartner.

class Meta[source]

Bases: object

fields = ['reference', 'booking_date', 'status', 'transport_type', 'incoterms', 'consignor', 'consignee', 'simulation']
model

alias of TransportChain

base_columns = {'booking_date': <django_tables2.columns.datecolumn.DateColumn object>, 'consignee': <django_tables2.columns.base.Column object>, 'consignor': <django_tables2.columns.base.Column object>, 'incoterms': <django_tables2.columns.base.Column object>, 'reference': <django_tables2.columns.base.Column object>, 'simulation': <django_tables2.columns.booleancolumn.BooleanColumn object>, 'status': <django_tables2.columns.base.Column object>, 'transport_type': <django_tables2.columns.base.Column object>}
before_render(request)[source]

Strip consignor/consignee links when the user cannot update partners.

class ferrosoft.apps.emiflow.tables.TransportLCCValueTable(*args, **kwargs)[source]

Bases: LCCValueTable

LCC value table specialised for transport operations.

class Meta[source]

Bases: Meta

model

alias of TransportLCCValue

base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'empty': <ferrosoft.apps.ferrobase.tables.columns.EmptyExpandColumn object>, 'lifecycle_category': <django_tables2.columns.base.Column object>, 'operation_emission': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'total_emission': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
get_delete_action_url(*args, **kwargs)[source]

Return the delete URL for the row’s transport LCC value.

Return type:

str | None

class ferrosoft.apps.emiflow.tables.TransportOperationTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

List table for TransportOperation with linkified reference and emission-grade badge.

class Meta[source]

Bases: object

fields = ['reference', 'name', 'activity_type', 'emitter', 'energy_carrier', 'emission_intensity__grade']
model

alias of TransportOperation

base_columns = {'activity_type': <django_tables2.columns.base.Column object>, 'emission_intensity__grade': <ferrosoft.apps.emiflow.tables.EmissionGradeColumn object>, 'emitter': <django_tables2.columns.base.Column object>, 'energy_carrier': <django_tables2.columns.base.Column object>, 'name': <django_tables2.columns.base.Column object>, 'reference': <django_tables2.columns.base.Column object>}
class ferrosoft.apps.emiflow.tables.TreatmentInputLineTable(*args, **kwargs)[source]

Bases: TreatmentLineTable

Treatment input lines (the materials consumed by a treatment).

class Meta[source]

Bases: object

fields = ['material', 'weight']
model

alias of TreatmentInputLine

orderable = False
template_name = 'ferrobase/bootstrap5/table/with_add_button.html'
base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'material': <django_tables2.columns.base.Column object>, 'weight': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
class ferrosoft.apps.emiflow.tables.TreatmentLCCValueTable(*args, **kwargs)[source]

Bases: LCCValueTable

LCC value table specialised for treatment operations.

class Meta[source]

Bases: Meta

model

alias of TreatmentLCCValue

base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'empty': <ferrosoft.apps.ferrobase.tables.columns.EmptyExpandColumn object>, 'lifecycle_category': <django_tables2.columns.base.Column object>, 'operation_emission': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'total_emission': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
get_delete_action_url(*args, **kwargs)[source]

Return the delete URL for the row’s treatment LCC value.

Return type:

str | None

class ferrosoft.apps.emiflow.tables.TreatmentLineTable(*args, **kwargs)[source]

Bases: CRUDTable

Base CRUD table for treatment lines, listing material and weight columns.

Subclassed for input, output, and operation-input/output variants; subclasses override the model and (for editable variants) the delete-URL resolution.

class Meta[source]

Bases: object

fields = ['material', 'weight']
model

alias of TreatmentLine

orderable = False
template_name = 'ferrobase/bootstrap5/table/with_add_button.html'
base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'material': <django_tables2.columns.base.Column object>, 'weight': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
class ferrosoft.apps.emiflow.tables.TreatmentOperationInputLineTable(*args, **kwargs)[source]

Bases: TreatmentLineTable

Treatment-operation input lines with delete actions.

class Meta[source]

Bases: object

fields = ['material', 'weight']
model

alias of TreatmentOperationInputLine

orderable = False
template_name = 'ferrobase/bootstrap5/table/with_add_button.html'
base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'material': <django_tables2.columns.base.Column object>, 'weight': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
get_delete_action_url(*args, **kwargs)[source]

Return the delete URL for the row’s input line.

Return type:

str | None

class ferrosoft.apps.emiflow.tables.TreatmentOperationOutputLineTable(*args, **kwargs)[source]

Bases: TreatmentLineTable

Treatment-operation output lines with allocated percentage and delete actions.

class Meta[source]

Bases: object

fields = ['material', 'weight', 'allocated_fraction']
model

alias of TreatmentOperationOutputLine

orderable = False
template_name = 'ferrobase/bootstrap5/table/with_add_button.html'
base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'allocated_fraction': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'material': <django_tables2.columns.base.Column object>, 'weight': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
get_delete_action_url(*args, **kwargs)[source]

Return the delete URL for the row’s output line.

Return type:

str | None

class ferrosoft.apps.emiflow.tables.TreatmentOperationTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

List table for TreatmentOperation with linkified reference and emission-grade badge.

class Meta[source]

Bases: object

fields = ['reference', 'status', 'name', 'emitter', 'energy_carrier', 'emission_intensity__grade']
model

alias of TreatmentOperation

base_columns = {'emission_intensity__grade': <ferrosoft.apps.emiflow.tables.EmissionGradeColumn object>, 'emitter': <django_tables2.columns.base.Column object>, 'energy_carrier': <django_tables2.columns.base.Column object>, 'name': <django_tables2.columns.base.Column object>, 'reference': <django_tables2.columns.base.Column object>, 'status': <django_tables2.columns.base.Column object>}
class ferrosoft.apps.emiflow.tables.TreatmentOutputLineTable(*args, **kwargs)[source]

Bases: TreatmentLineTable

Treatment output lines with an extra allocated-percentage column.

class Meta[source]

Bases: object

fields = ['material', 'weight']
model

alias of TreatmentOutputLine

orderable = False
template_name = 'ferrobase/bootstrap5/table/with_add_button.html'
base_columns = {'actions': <ferrosoft.apps.ferrobase.tables.columns.WidgetColumn object>, 'allocated_fraction': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'material': <django_tables2.columns.base.Column object>, 'weight': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}
class ferrosoft.apps.emiflow.tables.TreatmentTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

List table for Treatment with a linkified reference column.

class Meta[source]

Bases: object

fields = ['reference', 'status', 'booking_date', 'operation__name', 'operation__emitter', 'operation__energy_carrier']
model

alias of Treatment

base_columns = {'booking_date': <django_tables2.columns.datecolumn.DateColumn object>, 'operation__emitter': <django_tables2.columns.base.Column object>, 'operation__energy_carrier': <django_tables2.columns.base.Column object>, 'operation__name': <django_tables2.columns.base.Column object>, 'reference': <django_tables2.columns.base.Column object>, 'status': <django_tables2.columns.base.Column object>}
class ferrosoft.apps.emiflow.tables.WarehouseOverviewTable(data=None, order_by=None, orderable=None, empty_text=None, exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None, sequence=None, prefix=None, order_by_field=None, page_field=None, per_page_field=None, template_name=None, default=None, request=None, show_header=None, show_footer=True, extra_columns=None)[source]

Bases: Table

Aggregated, model-less warehouse overview keyed by material.

Rows come from a service-side aggregation (not the ORM), so the table declares no Meta.model and is non-orderable. Numeric columns format their values through format_mass so units render alongside the quantity.

class Meta[source]

Bases: object

orderable = False
base_columns = {'average_operation': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'average_total': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'emission_operation': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'emission_total': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>, 'material': <django_tables2.columns.base.Column object>, 'weight': <ferrosoft.apps.ferrobase.tables.columns.NumberColumn object>}

ferrosoft.apps.emiflow.tests module

ferrosoft.apps.emiflow.urls module

URL configuration for the emiflow application.

Exposes the dashboard index, per-domain CRUD URL bundles imported from ferrosoft.apps.emiflow.views, ad-hoc views for emission intensity, file import, reporting, treatment-operation allocation, and the settings pages, plus the DRF REST API mounted at api/ via ferrosoft.apps.emiflow.api.router.router. The app namespace is emiflow.

ferrosoft.apps.emiflow.widgets module

Custom Django form widgets used by emiflow forms and tables.

class ferrosoft.apps.emiflow.widgets.EditDeleteActionsWidget(*args, **kwargs)[source]

Bases: Widget

Row-actions widget rendering edit and delete buttons for a table row.

The button URLs are produced by per-row callables passed via edit_url and delete_url kwargs at construction time. When used inside a WidgetColumn the column monkey-patches render_args and render_kwargs onto the widget instance so the callables receive the row record; either callable may be None to suppress that button.

get_context(*args, **kwargs)[source]

Resolve edit_url and delete_url for the current row and inject them.

property media
template_name = 'emiflow/treatmentoperation/line_actions_widget.html'
class ferrosoft.apps.emiflow.widgets.EmissionFactorWidget(attrs=None)[source]

Bases: Widget

Hidden picker widget that surfaces a selected EmissionFactor.

The form posts the factor’s primary key, but the rendered template needs the full object to show a human-readable preview. This widget looks up the bound factor and exposes it to the template via the emission_factor context key while keeping the underlying input hidden.

get_context(name, value, attrs)[source]

Resolve the bound factor (if any) and force the input type to hidden.

property media
template_name = 'emiflow/picker/emission_factor/widget.html'