Resource types¶
Say a city runs three swimming pools and a theatre. The pools take bookings the same way: no payment, confirmed at once, one workflow. The theatre sells seats through a payment workflow. A resource type is what the three pools share: make a "Pool" type and a "Theatre" type, and each resource names its type.
A type answers the questions that belong to an order rather than to one resource. An order can hold bookings on several resources, paid once and driven by one workflow, so which workflow drives it cannot be a property of each resource: two resources could disagree. It is a property of their type.
What a type is¶
yoyaku_resource_type is a config entity with an id, a label, an optional
tenant and its field rules. Without a tenant, the type is shared and any
tenant's resources may use it. With one, it is that tenant's own. A resource
references its type and keeps everything about itself: its label, capacity,
slots, managers and the rest.
Types and tenants have admin forms of their own, and a type's field rules,
policies and managers are tabs of it. Managing a type takes administer
yoyaku: a resource's managers cannot change its type's settings, so how an
order is driven and paid stays out of their hands.
What a type carries¶
The type knows nothing about workflows or payment. A setting that is not the
type's own is kept in its third_party_settings, under the name of the module
that owns it:
yoyaku_orchestra: the workflow profile, which names the workflow and whether orders confirm themselves.yoyaku: the constraint policies attached to the type.
Settings that may differ between the resources of one order stay on the
resource: booking cutoff, lead time, capacity, prices and guarantees, the
calendar's selection rules, whether availability counts are shown. A type, a
tenant or the site can still decide them for every resource below it, with a
field rule. A resource holding inherit follows the rule,
and FieldRuleResolver::getEffectiveValue() is the one read path every getter
goes through.
Which workflow drives an order¶
WorkflowProfileResolver reads the workflow profile in this order, and the
first one set wins:
the order's resource type (third_party_settings of yoyaku_orchestra)
-> the type's tenant (third_party_settings of yoyaku_orchestra)
-> the site (yoyaku_orchestra.settings:workflow_profile)
The order's type is the type of its first booking still in play: a booking
edited out of the basket never decides it. An order holds bookings of one
workflow profile only. The SingleWorkflowProfilePerOrder constraint on the
order refuses a second, and "no profile" counts as a profile of its own. So a
cart may mix resources, and even types, as long as they are driven alike.
The workflow does not start on its own. A checkout surface, such as the calendar
"Book" button or the cart's checkout, or an operator calls
WorkflowStarterInterface::start($order), which starts it once and does nothing
when it is already running. Whether the order is free or paid is a branch inside
the workflow.
Which account takes the payment¶
PaymentAccountResolver::getAccountUuidForOrder() resolves the payment account
from the order's tenant, then from the site:
the order's tenant (its default_account_uuid)
-> the site (yoyaku_payment.settings:default_account_uuid)
An order belongs to one tenant, so the answer is always defined. An account is a merchant concern, which is why it hangs on the tenant and not on a shared type. A payment step in a workflow may still name an account of its own, which overrides the resolver for that step.