u
This commit is contained in:
179
docs/companyAutomat.md
Normal file
179
docs/companyAutomat.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# `companyAutomat`
|
||||
|
||||
> **Source File**: [`src/automats/companyAutomat.js`](file:///home/seb/src/dashfront/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
|
||||
|
||||
```ts
|
||||
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
|
||||
```js
|
||||
const initialState = {
|
||||
current: null,
|
||||
status: 'idle',
|
||||
error: null,
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Configuration & Options
|
||||
|
||||
```js
|
||||
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:
|
||||
|
||||
```js
|
||||
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
|
||||
|
||||
```jsx
|
||||
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
|
||||
|
||||
```js
|
||||
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()`.
|
||||
Reference in New Issue
Block a user