Laravel in Rotterdam
Laravel development for Rotterdam port and trade businesses
A Laravel development company for Rotterdam port, freight and trade firms. Permissions that belong to a relationship rather than a user, and one person who works for four companies.
A Rotterdam system has an identity problem before it has any other problem: the customer is a company, the user is a person, and the same person works for several companies.
Agents, brokers and forwarders in this port act for multiple principals as a matter of course. That makes the single-organisation-per-user shortcut - a company identifier on the users table, a role column beside it - wrong on the first day rather than eventually.
A Rotterdam permission lives on the membership
The structure that holds is a membership record joining a user to an organisation, carrying the role.
That one change makes every subsequent question answerable. What may this person do - for which organisation. Which shipments can they see - the ones belonging to the organisation they are acting for. Who at this company can approve an instruction - a query against memberships rather than against users.
It also means every authorisation check takes two arguments. A check that takes only the actor is a latent bug, and it will surface the first time a broker is added for a second client. Permission packages configured with a role on the user model have the same flaw, which is worth establishing before adopting one. Authorisation is not authentication is the general form of the argument.
Ending a relationship without erasing it
When somebody stops working for one of their companies, the membership ends - and ending is a date rather than a deletion.
Everything that person authorised while holding that membership stays attributed to them and to that organisation. A shipping instruction given eighteen months ago has to remain explicable, including who gave it and on whose behalf, long after they have moved on.
Deleting the membership breaks that attribution, and it breaks it silently, because the historical records still reference a user and an organisation that no longer have any relationship. Dated memberships keep the history intact and make the current state a query rather than a fact somebody has to maintain.
Rotterdam invitations are a privilege path
Letting customers add their own colleagues saves an enormous amount of support work and opens a route somebody will eventually test.
Three decisions make it safe. The invitation is bound to the address it was sent to, so forwarding it to somebody else does not work. It expires, so an old invitation in an inbox is not a permanent key. And it can only grant a role the inviter holds, because an invitation that grants more than the inviter has is privilege escalation with a pleasant interface.
None of those are difficult. All three are commonly missing, because the feature is built as a convenience rather than as part of the permission model.
Laravel scoping, including where there is no request
The organisation is resolved once per request and applied as a scope on the models that belong to one, so a query that forgets the condition returns nothing rather than everything.
The leak comes from the places with no request. A queued job that sends a document, a scheduled task that builds a report, a console command somebody runs to investigate a problem - none of these have a request to resolve an organisation from, and all of them touch data. Each has to carry its organisation and establish it before querying, and a job that does not is a defect regardless of whether it has caused an incident yet.
A Rotterdam company changes hands and its users do not
Companies merge, rename, split and get acquired, and the application finds out when somebody emails support.
The identity that survives all of it is the registration number, not the name, and designing around the name is the mistake that makes the rest hard. A renamed company is the same organisation with a new display name and a history that includes the old one - which matters, because invoices issued under the previous name must continue to show it. An acquisition is two organisations where one becomes a child of or is absorbed into the other, and absorbed means its data has to move somewhere deliberate rather than be orphaned.
Memberships are the part that needs explicit handling. When an organisation is absorbed, its users do not automatically belong to the acquirer, and assuming they do grants a group of people access to a larger dataset than they had yesterday. A merge is therefore an operation with a review step - these users move, these do not - rather than a foreign key update.
Splits run the other way and are rarer but messier. Two organisations now exist where one did, and historical records belong to the entity that existed at the time. Keeping the original organisation intact with its history, and creating the new entity forward from the split date, is almost always better than trying to divide the past.
Laravel API access for a Rotterdam partner's own systems
A customer's system needs to talk to the application, and the credential it uses should not be a person's.
Tokens issued to a named employee are the default because they are easy, and they fail in the obvious way - the employee leaves, their access is revoked correctly, and an integration that has been running for a year stops at three in the morning. A machine client belongs to the organisation, carries its own scopes, and has a lifecycle independent of any individual's employment.
Scopes are worth being strict about at the start, because they cannot be narrowed later without breaking somebody. A client that may read shipments and create bookings should have exactly that, not whatever the first integration happened to need plus everything added since. Scopes expressed as capabilities the organisation's own administrator can see and reason about is better than a list only developers understand.
Rotation has to be possible without downtime, which means two active credentials at once. A client gets a new secret, deploys it, and the old one is revoked afterwards - rather than a cutover that has to be coordinated across two companies in one maintenance window. Supporting that is a small amount of work at the start and the difference between a credential that gets rotated annually and one that has been in a configuration file since 2021.
Audit is by client rather than by user, and this is where the arrangement pays off. "The partner's booking system created these four hundred records" is a sentence the log can produce, and it cannot if they all arrived under the account of an operations manager who has not logged in personally for months.
Commercially
Scope fixed in writing before work starts, priced per project. The country-level ground - Dutch invoicing and e-invoicing, payment methods, the accessibility statement - is on the Netherlands market page.
Invoiced by Codefacture Yazılım A.Ş. in Türkiye, in euros by bank transfer, with no Turkish VAT under the export-of-services exemption.
