'', 'address' => '', 'postcode' => '', 'city' => '', 'country' => 'Österreich', 'phone' => '', 'email' => '', 'website' => '', // Firmenbuch / commercial register, and the court that keeps it. 'register_number' => '', 'register_court' => '', // UID / VAT identification number — the one field an invoice is // legally worthless without. 'vat_id' => '', 'bank_name' => '', 'iban' => '', 'bic' => '', 'logo_path' => '', // Payment terms in days, and the sentence printed under the total. 'payment_days' => 14, 'payment_terms' => '', ]; /** @return array */ public static function all(): array { $values = []; foreach (self::FIELDS as $key => $default) { $values[$key] = Settings::get(self::PREFIX.$key, $default); } return $values; } public static function get(string $field, mixed $default = null): mixed { return Settings::get(self::PREFIX.$field, $default ?? (self::FIELDS[$field] ?? null)); } /** @param array $values */ public static function put(array $values): void { foreach ($values as $key => $value) { // Unknown keys are dropped rather than stored. This is fed from a // form, and a forged field must not be able to write an arbitrary // setting into the same table the site-visibility switch lives in. if (! array_key_exists($key, self::FIELDS)) { continue; } Settings::set(self::PREFIX.$key, $value); } } /** * Is there enough here to put on an invoice? * * Not a nicety: an invoice without a company name, an address or a VAT * number is not a valid invoice in Austria, and issuing one is worse than * refusing to. The console asks this before it offers the button. * * @return array the fields that are missing, empty when ready */ public static function missingForInvoicing(): array { $required = ['name', 'address', 'postcode', 'city', 'vat_id']; return array_values(array_filter( $required, fn (string $field) => trim((string) self::get($field)) === '', )); } /** * The domestic VAT rate as a percentage, e.g. 20.0. * * The ONE place it is decided. App\Services\Billing\TaxTreatment reads it * from here rather than from config, because this page can change it and a * config value cannot — and two sources for one number is how an invoice * ends up disagreeing with the checkout that produced it. * * Falls back to the .env-derived config until an operator has saved a rate, * so an installation that already carries CLUPILOT_TAX_PERCENT keeps * working unchanged. Same shape as App\Support\ProvisioningSettings. */ public static function taxRate(): float { return (float) Settings::get('company.tax_rate', (float) config('provisioning.tax.rate_percent', 20)); } }