Files
dashfront/docs/unitAutomat.md
2026-09-23 17:24:33 +02:00

4.6 KiB

unitAutomat

Source File: src/automats/unitAutomat.js
Registration Key: 'unit' (Automat.get('unit') / window.__AUTOMATS__.get('unit'))


1. Overview & Purpose

unitAutomat manages the active company's allowed units of measure (e.g. Stück, kg, Meter, Liter). Units are attached to articles and delivery lineitems to ensure consistent units of measure and price calculation across the catalog and invoices.

Core Responsibilities

  • Company Scoping: Caches all measurement units for the currently selected company.
  • Alphabetical Sorting: Automatically sorts units by their display symbol or SI code using German collation.
  • Autonomous Bootstrap: Automatically loads whenever authAutomat transitions to 'authed' with a selected company, and resets when the session ends.
  • Full Unit Maintenance: Upserting (creating or modifying) and deleting units by SI key.

2. State Structure

interface Unit {
  si: string;          // International standard code (e.g. 'C62', 'KGM', 'LTR')
  symbol?: string;     // Display symbol (e.g. 'Stk', 'kg', 'l')
  name?: string;       // Full name (e.g. 'Stück', 'Kilogramm')
  [key: string]: any;
}

interface UnitState {
  /** Alphabetically sorted list of measurement units. */
  units: Array<Unit>;
  /** Async lifecycle status. */
  status: 'idle' | 'loading' | 'succeeded' | 'failed';
  /** Last error message if load failed. */
  error: string | null;
}

Initial State

const initialState = {
  units: [],
  status: 'idle',
  error: null,
};

3. Configuration & Options

new Automat(initialState, actions, { name: 'unit' });
  • name: 'unit'
  • persist: false

4. Actions & Methods

Access actions via unitAutomat.actions.<method>().

load(): Promise<Array<Unit>>

Fetches all units for the active company via GET /api/unit. Sorts them, updates state, and sets status to 'succeeded'.

upsert(body: object): Promise<Array<Unit>>

Creates or updates a unit via POST /api/unit/upsert. Merges the updated unit into state.units by matching on its si property, re-sorts the list, updates state, and returns the full updated list.

delete(si: string): Promise<void>

Deletes a unit by SI code via DELETE /api/unit/:si and filters it out of state.units.

reset(): void

Resets the state back to initialState.


5. Cross-Automat Subscriptions

unitAutomat tracks authentication changes automatically:

authAutomat.subscribe((auth) => {
  const companyId = auth.user?.selectedCompanyId;
  if (auth.status === 'authed' && companyId) {
    void unitAutomat.actions.load();
  } else if (auth.status === 'anon') {
    unitAutomat.actions.reset();
  }
});

6. Usage Examples

A. React PureComponent Unit Select Dropdown

import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import unitAutomat from '../automats/unitAutomat';

export class UnitSelect extends PureComponent {
  static propTypes = {
    value: PropTypes.string,
    onChange: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      units: unitAutomat.state.units,
      status: unitAutomat.state.status,
    };
  }

  componentDidMount() {
    this.unsub = unitAutomat.subscribe(this, ['units', 'status']);
    if (this.state.status === 'idle') {
      unitAutomat.actions.load().catch(console.error);
    }
  }

  componentWillUnmount() {
    this.unsub?.();
  }

  render() {
    const { units, status } = this.state;
    const { value, onChange } = this.props;

    if (status === 'loading') {
      return <select disabled><option>Einheiten laden...</option></select>;
    }

    return (
      <select value={value ?? ''} onChange={(e) => onChange(e.target.value)}>
        <option value="">-- Einheit wählen --</option>
        {units.map((u) => (
          <option key={u.si} value={u.symbol || u.si}>
            {u.symbol || u.si} {u.name ? `(${u.name})` : ''}
          </option>
        ))}
      </select>
    );
  }
}

B. Upserting a Unit

import unitAutomat from '../automats/unitAutomat';

async function addCustomUnit(si, symbol, name) {
  try {
    await unitAutomat.actions.upsert({ si, symbol, name });
    console.log('Unit saved successfully');
  } catch (err) {
    console.error('Failed to save unit:', err);
  }
}

7. Best Practices & Gotchas

  1. Unique Key: Units are uniquely identified by their si property (not an integer ID).
  2. Sort Guarantee: Units in state.units are sorted alphabetically based on u.symbol ?? u.si.