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

5.6 KiB

companyAutomat

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


1. Overview & Purpose

companyAutomat stores the active company configuration and metadata for the authenticated user's selected tenant. It provides essential company-wide parameters needed throughout Dashfront, such as default tax rates (standard and reduced), legal company details, default language, and the root category ID of the catalog hierarchy.

Core Responsibilities

  • Company Configuration: Caches legal details, tax rates (taxrate, reducedtaxrate), and catalog root category ID (rootCategoryId).
  • Data Normalization: Normalizes backend company payloads via normalizeCompany() for predictable shape in forms and calculations.
  • Cross-Automat Triggering: Changes in companyAutomat drive categoryAutomat to fetch the company's category tree.
  • Autonomous Bootstrap: Automatically loads whenever authAutomat has an active selectedCompanyId and resets on logout.

2. State Structure

interface NormalizedCompany {
  id: number | string;
  name: string;
  rootCategoryId: number | string | null;
  taxrate: number;         // Standard VAT rate (e.g. 19.00)
  reducedtaxrate: number;  // Reduced VAT rate (e.g. 7.00)
  iso639: string;          // Default language code (e.g. 'de')
  street?: string;
  zip?: string;
  city?: string;
  country?: string;
  iban?: string;
  bic?: string;
  taxId?: string;
  vatId?: string;
  [key: string]: any;
}

interface CompanyState {
  /** Normalized details of currently selected company. */
  current: NormalizedCompany | null;
  /** Async lifecycle status. */
  status: 'idle' | 'loading' | 'succeeded' | 'failed';
  /** Last error message if load/update failed. */
  error: string | null;
}

Initial State

const initialState = {
  current: null,
  status: 'idle',
  error: null,
};

3. Configuration & Options

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

4. Actions & Methods

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

load(): Promise<NormalizedCompany | null>

Determines the selected company ID from authAutomat.state.user?.selectedCompanyId. If none is selected, returns null. Otherwise, queries GET /api/company/:id, normalizes the data, updates state.current, and sets status to 'succeeded'.

update(payload: object): Promise<NormalizedCompany>

Persists updated company settings via PUT /api/company/:id, re-fetches authoritative data, normalizes it, updates state.current, and returns the updated company object.

reset(): void

Resets state back to initialState.


5. Exported Standalone Selectors

Exported plain helper functions for querying company state without requiring a React component:

Selector Return Type Description
selectCurrentCompany() NormalizedCompany | null Returns the entire current company object.
selectCompanyId() string | number | null The ID of the currently selected company.
selectCompanyStatus() 'idle'|'loading'|'succeeded'|'failed' Current status of the company store.
selectCompanyName() string | undefined Company legal or trade name.
selectCompanyIso639() string | undefined ISO language code (e.g. 'de').
selectCompanyTaxrate() number | null Standard VAT rate (e.g. 19.0).
selectCompanyReducedTaxrate() number | null Reduced VAT rate (e.g. 7.0).
selectCompanyRootCategoryId() number | string | null ID of the root category node.

6. Cross-Automat Subscriptions

companyAutomat automatically tracks authentication changes:

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

7. Usage Examples

A. React PureComponent Tax Rate Calculation

import { PureComponent } from 'react';
import companyAutomat, { selectCompanyTaxrate } from '../automats/companyAutomat';

export class PriceDisplay extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      taxrate: selectCompanyTaxrate() ?? 19.0,
    };
  }

  componentDidMount() {
    this.unsub = companyAutomat.subscribe(this, (state) => ({
      taxrate: state.current?.taxrate ?? 19.0,
    }));
  }

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

  render() {
    const { netPrice } = this.props;
    const grossPrice = netPrice * (1 + this.state.taxrate / 100);

    return (
      <div>
        <span>Netto: {netPrice.toFixed(2)} €</span>
        <strong>Brutto (inkl. {this.state.taxrate}% MwSt): {grossPrice.toFixed(2)} €</strong>
      </div>
    );
  }
}

B. Pure Function / Utility Usage

import { selectCompanyId, selectCompanyRootCategoryId } from '../automats/companyAutomat';

export function getCompanyUploadUrl() {
  const companyId = selectCompanyId();
  if (!companyId) throw new Error('Kein Unternehmen aktiv.');
  return `/api/company/${companyId}/upload`;
}

8. Best Practices & Gotchas

  1. Tax Calculations: Always query selectCompanyTaxrate() or selectCompanyReducedTaxrate() instead of hardcoding 19% or 7%.
  2. Catalog Roots: Don't assume root category is 1 or null; always use selectCompanyRootCategoryId().