{COMPANY_NAME}. Please download using the button below:'; private const ESTIMATE_MAIL_BODY = 'You have received a new estimate from {COMPANY_NAME}. Please download using the button below:'; private const PAYMENT_MAIL_BODY = 'Thank you for the payment. Please download your payment receipt using the button below:'; /** Address blocks printed on the documents; placeholders filled at render. */ private const BILLING_ADDRESS_FORMAT = '
{BILLING_ADDRESS_STREET_1}
{BILLING_ADDRESS_STREET_2}
{BILLING_CITY} {BILLING_STATE}
{BILLING_COUNTRY} {BILLING_ZIP_CODE}
{BILLING_PHONE}
'; private const SHIPPING_ADDRESS_FORMAT = '{SHIPPING_ADDRESS_STREET_1}
{SHIPPING_ADDRESS_STREET_2}
{SHIPPING_CITY} {SHIPPING_STATE}
{SHIPPING_COUNTRY} {SHIPPING_ZIP_CODE}
{SHIPPING_PHONE}
'; private const COMPANY_ADDRESS_FORMAT = '{COMPANY_ADDRESS_STREET_1}
{COMPANY_ADDRESS_STREET_2}
{COMPANY_CITY} {COMPANY_STATE}
{COMPANY_COUNTRY} {COMPANY_ZIP_CODE}
{COMPANY_PHONE}
'; /** The payer block on a receipt, spaced differently from the billing one. */ private const PAYMENT_CUSTOMER_ADDRESS_FORMAT = '{BILLING_ADDRESS_STREET_1}
{BILLING_ADDRESS_STREET_2}
{BILLING_CITY} {BILLING_STATE} {BILLING_ZIP_CODE}
{BILLING_COUNTRY}
{BILLING_PHONE}
'; public function __construct( private readonly CompanyDefaultsProvisioner $companyDefaultsProvisioner, private readonly CompanyDataPurger $companyDataPurger, ) {} /** * Furnish a newly created company. * * Order is fixed: the owner role first, so the creator has something to be * assigned; then the reference data; then the preference sheet, which is * where the chosen currency lands. */ public function setupDefaults(Company $company, int $currencyId = 13): bool { $this->setupRoles($company); $this->companyDefaultsProvisioner->provision($company); $this->setupDefaultSettings($company, $currencyId); return true; } /** * Create the company's `owner` role and grant it the whole ability * catalogue — every entry in the configuration, against the subject model * the entry names. * * Roles live inside a company's scope, so the scope is moved onto this * company first and left there for whatever the caller does next. */ public function setupRoles(Company $company): void { BouncerFacade::scope()->to($company->id); $owner = BouncerFacade::role()->firstOrCreate([ 'name' => self::OWNER_ROLE, 'title' => self::OWNER_ROLE_TITLE, 'scope' => $company->id, ]); foreach (config('abilities.abilities') as $entry) { BouncerFacade::allow($owner)->to($entry['ability'], $entry['model']); } } /** * Wind a company up. * * The purger clears everything filed against the company first; what is * left here is the company's own furniture — its scoped roles, the * memberships pointing at it, its preferences, and the row itself. The * member accounts survive: only the link between them and the company is * cut. */ public function delete(Company $company): bool { $this->companyDataPurger->purge($company); Role::query() ->when($company->id, function ($query) use ($company) { $query->where('scope', $company->id); }) ->get() ->each(function ($role) { $role->delete(); }); $company->users()->detach(); $company->settings()->delete(); $company->delete(); return true; } /** * The preference sheet a company starts out with. * * Two of these are historical rather than sensible and are kept on * purpose: the time zone defaults to `Asia/Kolkata`, and outgoing mail is * addressed from the project's own no-reply address until an owner changes * it. `bulk_exchange_rate_configured` starts out done, which keeps fresh * companies out of the exchange-rate backfill. */ private function setupDefaultSettings(Company $company, int $currencyId): void { CompanySetting::setSettings([ 'invoice_mail_body' => self::INVOICE_MAIL_BODY, 'estimate_mail_body' => self::ESTIMATE_MAIL_BODY, 'payment_mail_body' => self::PAYMENT_MAIL_BODY, 'invoice_company_address_format' => self::COMPANY_ADDRESS_FORMAT, 'invoice_shipping_address_format' => self::SHIPPING_ADDRESS_FORMAT, 'invoice_billing_address_format' => self::BILLING_ADDRESS_FORMAT, 'estimate_company_address_format' => self::COMPANY_ADDRESS_FORMAT, 'estimate_shipping_address_format' => self::SHIPPING_ADDRESS_FORMAT, 'estimate_billing_address_format' => self::BILLING_ADDRESS_FORMAT, 'payment_company_address_format' => self::COMPANY_ADDRESS_FORMAT, 'payment_from_customer_address_format' => self::PAYMENT_CUSTOMER_ADDRESS_FORMAT, 'currency' => $currencyId, 'time_zone' => 'Asia/Kolkata', 'language' => 'en', 'fiscal_year' => '1-12', 'carbon_date_format' => 'Y/m/d', 'moment_date_format' => 'YYYY/MM/DD', 'carbon_time_format' => 'H:i', 'moment_time_format' => 'HH:mm', 'invoice_use_time' => 'NO', 'notification_email' => 'noreply@invoiceshelf.com', 'notify_invoice_viewed' => 'NO', 'notify_estimate_viewed' => 'NO', 'tax_per_item' => 'NO', 'discount_per_item' => 'NO', 'invoice_email_attachment' => 'NO', 'estimate_email_attachment' => 'NO', 'payment_email_attachment' => 'NO', 'retrospective_edits' => 'allow', 'invoice_number_format' => '{{SERIES:INV}}{{DELIMITER:-}}{{SEQUENCE:6}}', 'credit_note_number_format' => '{{SERIES:CN}}{{DELIMITER:-}}{{SEQUENCE:6}}', 'estimate_number_format' => '{{SERIES:EST}}{{DELIMITER:-}}{{SEQUENCE:6}}', 'payment_number_format' => '{{SERIES:PAY}}{{DELIMITER:-}}{{SEQUENCE:6}}', 'estimate_set_expiry_date_automatically' => 'YES', 'estimate_expiry_date_days' => 7, 'invoice_set_due_date_automatically' => 'YES', 'invoice_due_date_days' => 7, 'bulk_exchange_rate_configured' => 'YES', 'estimate_convert_action' => 'no_action', 'automatically_expire_public_links' => 'YES', 'link_expiry_days' => 7, ], $company->id); } }