This WS is design to create relationship for reorganized legal entities by NHS admin using DS. Before the request is created new LE must be created (in case if needed) and employee from old legal entity must be registered in the new one. After that NHS admin must set relationship via signed content. After basic validation
Specification
Schemas:
"""
A condition to be used against `LegalEntity` object types. All fields are tested for equality and combined with a logical ‘and.’
"""
input LegalEntityFilter {
"The ID of an object"
databaseId: UUID
"A unique identification number of a legal entity in the State Register of Enterprises and Organizations of Ukraine."
edrpou: String
"In case legal entity was verified by NHS employee, nhsVerified should be true."
nhsVerified: Boolean
"In case legal entity was reviewed by NHS employee, nhsReview should be true."
nhsReviewed: Boolean
"Legal entity type. The value should be present in the `LEGAL_ENTITY_TYPE` dictionary."
type: [String]
"Conditions to check on fields of the object’s `addresses` field."
residenceAddress: AddressFilter
"Whether the legal entity is present in a unified state register or not?"
edrVerified: Boolean
"Legal entity status"
status: LegalEntityStatus
"EDRData filter"
edrData: EDRDataFilter
}
"""
A condition to be used against `EDRData` object types. All fields are tested for equality and combined with a logical ‘and.’
"""
input EDRDataFilter {
"The ID of an object"
id: ID!
"A unique identification number of a legal entity in the State Register of Enterprises and Organizations of Ukraine."
edrpou: String
"Official name of legal entity"
name: String
"Conditions to check on fields of the object’s `addresses` field."
registrationAddress: AddressFilter
"Flag whether `edr_data` is actual or not?"
isActive: Boolean
}
"""
Methods to use when ordering `LegalEntity`.
"""
enum LegalEntityOrderBy {
"Sort legal entity by edrpou in ascending order."
EDRPOU_ASC
"Sort legal entity by edrpou in descending order."
EDRPOU_DESC
"Sort legal entity by inserted at in ascending order."
INSERTED_AT_ASC
"Sort legal entity by inserted at in descending order."
INSERTED_AT_DESC
"Sort legal entity by nhs review at in ascending order."
NHS_REVIEWED_ASC
"Sort legal entity by nhs review at in descending order."
NHS_REVIEWED_DESC
"Sort legal entity by nhs verified at in ascending order."
NHS_VERIFIED_ASC
"Sort legal entity by nhs verified at in descending order."
NHS_VERIFIED_DESC
"Sort legal entity by status at in ascending order."
STATUS_ASC
"Sort legal entity by status at in descending order."
STATUS_DESC
}
"""
A connection to a list of `LegalEntity` values.
"""
type LegalEntityConnection {
"Information to aid in pagination."
pageInfo: PageInfo!
"A list of nodes."
nodes: [LegalEntity]
"A list of edges."
edges: [LegalEntityEdge]
}
"""
Reads and enables pagination through a set of `LegalEntity`.
"""
type LegalEntityEdge {
"The item at the end of the edge."
node: LegalEntity!
"A cursor for use in pagination."
cursor: String!
}
"""
Input for `nhsVerifyLegalEntity` mutation.
"""
input NhsVerifyLegalEntityInput {
"Legal entity database unique identifier."
id: ID!
"Whether `LegalEntity` was verified or not?"
nhsVerified: Boolean!
}
"""
Return type for `nhsVerifyLegalEntity` mutation.
In order to verify legal entity user must have a scope `legal_entity:nhs_verify`.
"""
type NhsVerifyLegalEntityPayload {
"Payload for legal entity."
legalEntity: LegalEntity
}
"""
Input for `nhsReviewLegalEntity` mutation.
"""
input NhsReviewLegalEntityInput {
"Legal entity database unique identifier."
id: ID!
}
"""
Return type for `nhsReviewLegalEntity` mutation.
In order to review legal entity user must have a scope `legal_entity:nhs_verify`.
"""
type NhsReviewLegalEntityPayload {
"Payload for legal entity."
legalEntity: LegalEntity
}
"""
Input for `nhsCommentLegalEntity` mutation.
"""
input NhsCommentLegalEntityInput {
"Legal entity database unique identifier."
id: ID!
"Comment for MSP to change legal entity"
nhsComment: String!
}
"""
Return type for `nhsCommentLegalEntity` mutation.
In order to comment for legal entity user must have a scope `legal_entity:nhs_verify`.
"""
type NhsCommentLegalEntityPayload {
"Payload for legal entity."
legalEntity: LegalEntity
}
"""
Input for `updateLegalEntityStatus` mutation.
"""
input UpdateLegalEntityStatusInput {
"Legal entity database unique identifier."
id: ID!
"Legal entity status"
status: LegalEntityUpdateableStatus!
"Why `LegalEntity` status is changing?"
reason: String
}
"""
List of legal entities statuses allowed to update by NHS.
"""
enum LegalEntityUpdateableStatus {
"Denotes that legal entity is active."
ACTIVE
"Denotes that legal entity is suspended and has limited rights for actions in ehealth system."
SUSPENDED
}
"""
Return type for `UpdateLegalEntityStatus` mutation.
In order to update the legal entity status user must have a scope `legal_entity:update`.
"""
type UpdateLegalEntityStatusPayload {
"Payload for legalEntity."
legalEntity: LegalEntity
}
"""
Input for `deactivateLegalEntity` mutation.
"""
input DeactivateLegalEntityInput {
"Legal entity database unique identifier."
id: ID!
}
"""
Return type for `deactivateLegalEntity` mutation.
In order to deactivate legal entity user must have a scope `legal_entity:deactivate`.
"""
type DeactivateLegalEntityPayload {
"Payload for legalEntity."
legalEntity: LegalEntity
}
"""
Legal Entity contains data about medical service providers, pharamcies and MISes.
In order to obtain details user must have a scope `legal_entity:read`.
NHS admin can verify Legal Entity having a scope `legal_entity:nhs_verify`.
Also it's possible to deactivate Legal Entity having a scope `legal_entity:deactivate`
"""
type LegalEntity implements Node {
"The ID of an object"
id: ID!
"Primary key identifier from the database"
databaseId: UUID!
"Full official name of legal entity also the name to show on public sources [as map, portal etc]"
name: String @deprecated(reason: "Use `name` from type `EDRData`.")
"Legal entity short name."
shortName: String @deprecated(reason: "Use `shortName` from type `EDRData`.")
"Legal entity public name."
publicName: String
@deprecated(reason: "Use `publicName` from type `EDRData`.")
"Business form."
legalForm: String @deprecated(reason: "Use `legalForm` from type `EDRData`.")
"Ukrainian Industry Classification System, there is a check that at least one of next kveds is input: 86.10, 86.21, 47.73."
kveds: [String] @deprecated(reason: "Use `kveds` from type `EDRData`.")
"Legal entity type. The value should be present in the `LEGAL_ENTITY_TYPE` dictionary."
type: String!
"A unique identification number of a legal entity in the State Register of Enterprises and Organizations of Ukraine."
edrpou: String! @fake(randexp: "^[0-9]{8,10}$")
"Legal entity status, is set automatically"
status: LegalEntityStatus!
"Reason why status was set"
statusReason: String
"Legal entity residence address."
residenceAddress: Address
"Legal entity addresses."
addresses: [Address]
@deprecated(
reason: "Use `residenceAddress` and `registrationAddress` from type `EDRData`."
)
"Legal entity phone number."
phones: [Phone]!
"email to contact person in charge from legal entity"
email: String! @fake(locale: "uk", method: "internet.email")
"Treasury registration code."
receiverFundsCode: String @fake(randexp: "^[0-9]{14}$")
"Dictionary OWNER_PROPERTY_TYPE State or private type of legal entity"
ownerPropertyType: String @deprecated(reason: "No longer supported")
"Reads through a set of associated `LegalEntityLicense`."
licenses(
"A condition to be used in determining which values should be returned by the collection."
filter: LegalEntityLicenseFilter
"The method to use when ordering collection items."
orderBy: LegalEntityLicenseOrderBy
"Read all values in the set after (below) this cursor."
after: String
"Read all values in the set before (above) this cursor."
before: String
"Only read the first _n_ values of the set."
first: Int
"Only read the last _n_ values of the set."
last: Int
): LegalEntityLicenseConnection!
"Accreditation of legal entity."
accreditations: [LegalEntityAccreditation]
"In case legal entity is verified by MIS, misVerified = true"
misVerified: LegalEntityMisVerificationStatus!
"Change requests for this `LegalEntity` in order to verify it."
nhsComment: String @fake(locale: "uk", method: "lorem.sentenses")
"Whether this `LegalEntity` was reviewed or not?"
nhsReviewed: Boolean
"Whether this `LegalEntity` was verified by NHS or not?"
nhsVerified: Boolean
"Date and time when `LegalEntity` has lost NHS verification."
nhsUnverifiedAt: DateTime
"legal entity website"
website: String @fake(locale: "uk", method: "internet.url")
"legal owner of legal entity [нформація про власника ЗОЗ, для ФОП не заповнюється]"
beneficiary: String
@fake(locale: "uk", format: "{{name.findName}} {{name.jobTitle}}")
"Legal entity archive information, date and place"
archives: [LegalEntityArchive]
"System information: date and time when legal entity was inserted to DB."
insertedAt: DateTime!
"First employee with employee_type='OWNER ordered by UPDATED_AT_DESC"
owner: Employee!
"Whether the legal entity is present in a unified state register or not?"
edrVerified: Boolean
"Data that are synchronised with governmental legal entity database"
edrData: EDRData
"Employees wich belong to legal entity"
employees(
"A condition to be used in determining which values should be returned by the collection."
filter: EmployeeFilter
"The method to use when ordering collection items."
orderBy: EmployeeOrderBy
"Read all values in the set after (below) this cursor."
after: String
"Read all values in the set before (above) this cursor."
before: String
"Only read the first _n_ values of the set."
first: Int
"Only read the last _n_ values of the set."
last: Int
): EmployeeConnection!
"Divisions within this legal entity."
divisions(
"A condition to be used in determining which values should be returned by the collection."
filter: DivisionFilter
"The method to use when ordering collection items."
orderBy: DivisionOrderBy
"Read all values in the set after (below) this cursor."
after: String
"Read all values in the set before (above) this cursor."
before: String
"Only read the first _n_ values of the set."
first: Int
"Only read the last _n_ values of the set."
last: Int
): DivisionConnection!
"Main/Active legal entity after merging legal entities."
mergedToLegalEntity: RelatedLegalEntity
"Legal entities that are not active anymore and that were merged to this one."
mergedFromLegalEntities(
"A condition to be used in determining which values should be returned by the collection."
filter: RelatedLegalEntityFilter
"The method to use when ordering collection items."
orderBy: RelatedLegalEntityOrderBy
"Read all values in the set after (below) this cursor."
after: String
"Read all values in the set before (above) this cursor."
before: String
"Only read the first _n_ values of the set."
first: Int
"Only read the last _n_ values of the set."
last: Int
): RelatedLegalEntityConnection!
"Reads and enables pagination through a set of `Healthcare service`."
healthcareServices(
"A condition to be used in determining which values should be returned by the collection."
filter: HealthcareServiceFilter
"The method to use when ordering collection items."
orderBy: HealthcareServiceOrderBy
"Read all values in the set after (below) this cursor."
after: String
"Read all values in the set before (above) this cursor."
before: String
"Only read the first _n_ values of the set."
first: Int
"Only read the last _n_ values of the set."
last: Int
): HealthcareServiceConnection!
}
"""
List of legal entities statuses.
"""
enum LegalEntityStatus {
"Denotes that legal entity is active."
ACTIVE
"Denotes that legal entity is closed. This state is irreversible."
CLOSED
"Denotes that legal entity is reorganised."
REORGANISED
"Denotes that legal entity is suspended and has limited rights for actions in ehealth system."
SUSPENDED
}
"""
List of Mis Verification status.
"""
enum LegalEntityMisVerificationStatus {
"Status for not verified legal entity by MIS"
NOT_VERIFIED
"Status for verified legal entity by MIS"
VERIFIED
}
"""
Legal entity accreditation information.
"""
type LegalEntityAccreditation {
"Accreditation category."
category: String
"The issue date of accreditation."
issuedDate: Date
"The expiration date of accreditation."
expiryDate: Date @fake(locale: "uk", method: "date.future", args: [2])
"Accreditation order number."
orderNo: String
"Accreditation order date."
orderDate: Date
}
"""
Inforamtion of transferring paper documents of legal entities to archive.
"""
type LegalEntityArchive {
"the date when paper documents were transferred to archive"
date: Date
"the address of building where paper documents are"
place: String
}
"""
Data that are synchronised with governmental legal entity database
"""
type EDRData {
"The ID of an object"
id: ID!
"Primary key identifier from the database"
databaseId: UUID!
"Edr key"
edrId: Int!
"Full official name of legal entity also the name to show on public sources [as map, portal etc]"
name: String! @fake(locale: "uk", method: "company.companyName")
"Legal entity short name."
shortName: String
@fake(locale: "uk", method: "company.companyName", args: [0])
"Legal entity public name."
publicName: String! @fake(locale: "uk", method: "company.companyName")
"Business form."
legalForm: String @fake(locale: "uk", method: "company.companySuffix")
"Unified Register of Businesses and Organizations"
edrpou: String! @fake(randexp: "^[0-9]{8,10}$")
"Ukrainian Industry Classification System, there is a check that at least one of next kveds is input: 86.10, 86.21, 47.73."
kveds: [EDRkved]!
"Official registraction address of legalEntity"
registrationAddress: EDRAddress
"Flag whether `edr_data` is actual or not?"
isActive: Boolean!
"Legal entity status in governmantal database"
state: Int!
"Date and time when record was inserted"
insertedAt: DateTime!
"Date and time when record was updated"
updatedAt: DateTime!
}
"""
Ukrainian Industry Classification System.
"""
type EDRkved {
"Kved number that is allowed for legal entity"
code: String
"Flag if `kved` is promary or not"
isPrimary: Boolean
}
"""
Address structure in EDR.
"""
type EDRAddress {
"Full address"
address: String
"Address's zip code"
zip: String
"The name of Country"
country: String
"Structured full address"
parts: EDRAddressParts
}
"""
Structured full address in EDR
"""
type EDRAddressParts {
"Administrative Territorial Unit"
atu: String
"Room number"
num: String
"Building number"
building: String
"House number"
house: String
"Administrative Territorial Unit Code"
atuCode: String
"House type"
houseType: String
"Name of the street"
street: String
"Building type"
buildingType: String
"Room Type"
numType: String
}
Link
Посилання на Apiary або Swagger
Resource
Посилання на ресурс, наприклад: /api/persons/create
Scope
Scope для доступу
Components
Зазначається перелік бізнес компонентів, які використовують цей метод, наприклад: ePrescription
Microservices
Перелік мікросервісів, які використовує метод API, наприклад: Auth, ABAC
Protocol type
Тип протоколу, який використовується запитом, наприклад: SOAP | REST
Request type
Тип запиту API, наприклад: GET, POST, PATCH…
Sync/Async
Метод є синхронним чи асинхронним?
Public/Private/Internal
Потрібно зазначити тип методу за ступенем доступності
Logic
API paragraph not found
Input parameters
Input is signed data in PKCS7 format. The data must be unpacked and validated using Json Schema.
Fields to sign:
Input parameter
Values
Type
Description
Example
Input parameter
Values
Type
Description
Example
merged_from_legal_entity
id
uuid
required
name
string
required
edrpou
string
required
merged_to_legal_entity
id
id
required
name
string
required
edrpou
string
required
reason
sring
required
Request structure
API paragraph not found
Authorize
Verify the validity of access token
in case of error return 401 ('Access denied')
Check user scope legal_entity:merge in order to perform this action
in case of error generate 401 response ('Invalid scopes')
Headers
API paragraph not found
Request data validation
Validate Digital signature
Decode content that is encrypted in an electronic digital signature. Use Digital signature WS. Method checks digital signature and returns result.
We need to check DS.
Get client_id from token. Find prm.legal_entities by client_id
Check EDRPOU matches prm.legal_entities.EDRPOU
Check if EDRPOU in Certificate details exists and not empty
Check if Certificate_details.EDRPOU=prm.legal_entities.EDRPOU
In case validation fails - generate 422 error
Get parties.tax_id using party_users.party_id by user_id.
Compare DRFO in Certificate with party.tax_id
Convert DRFO and TAX_ID to uppercase
Compare DRFO and TAX_ID as Cyrillic letters
Convert DRFO to Cyrillic and compare as Cyrillic letters
In case validation fails - generate 422 error
Verify role
Extract from token:
Validate client_id (is_blocked=false)
in case of error return 403 Error ('Client is blocked')
Check client_id is active
in case error return 403 - ('Client is not active')
Validate request
Validate merged_tolegal_entity
Check legal entity exists and status='active'
in case of error return 409 error view $merged_to.id ('Merged to legal entity must be active')
Validate merged_to legal entity is not an active merged_from legal entity (select * from related_legal_entities where merged_from_id=$merged_to_id and is_active=true) returns empty result
in case of error return 409 error view $merged_to.id ('Merged to legal entity is in the process of reorganisation itself')
Validate merged_to.name from request=prm.legal_entites.$merged_to.id.name
in case of error return 422 error view $merged_to.name ('Invalid legal entity name')
Validate merged_to.edrpou from request=prm.legal_entites.$merged_to.id.edrpou
in case of error return 422 error view $merged_to.edrpou ('Invalid legal entity edrpou')
Validate merged_from legal_entity
Check merged from legal entity exists and status='active' or 'suspended'
in case of error return 409 error view $merged_from.id ('Merged from legal entity must be active or suspended')
Validate merged from legal entity is not in the process of reorganization (select * from related_legal_entities where merged_from_id=$merged_from_id and is_active=true) returns empty result
in case of error return 409 error view $merged_from.id ('Merged from legal entity is already in the process of reorganisation')
Validate merged from legal_entity.name from request=prm.legal_entites.$merged_from.id.name
in case of error return 422 error view $merged_from.name ('Invalid legal entity name')
Validate merged from legal_entity.edrpou from request=prm.legal_entites.$merged_from.id.edrpou
in case of error return 422 error view $merged_from.edrpou ('Invalid legal entity edrpou')
in case of error return 422 error view ('Legator and successor legal entities must be different')
Validate merged_to.legal_entity_type and merged_from.legal_entity_type. Valid type transitions see in the table below.
in case of error return 422 error view ('Invalid legal entity type')
Valid type transitions for legal entities
merged_from
merged_to
merged_from
merged_to
MSP
PRIMARY_CARE, MSP
MSP_PHARMACY
MSP_PHARMACY
PHARMACY
PHARMACY
PRIMARY_CARE
PRIMARY_CARE, MSP
OUTPATIENT
OUTPATIENT
EMERGENCY
EMERGENCY
Processing
Create job
Async job must be created and started (scope to get job - legal_entity_merge_job:read)
Job has next structure
field
type
M/O
description
field
type
M/O
description
id
UUID
M
job ID
status
enum
PENDING
ERROR
PROCESSED
M
job status
started_at
TIMESTAMP
M
the time when job was started
ended_at
TIMESTAMP
O
the time when job was ended (if any)
merged_from_legal_entity
jsonb
M
merged_to_legal_entity
jsonb
M
The job must do next steps:
Save signed content to media storage
Get url for request upload.
Upload signed content to media storage
Search employees (employee_type='DOCTOR')
For each employee with type DOCTOR from merged from legal_entity take employee.speciality_officio and employee.party
select id as merged_from_employee_id, speciality.speciality, party_id from employees where legal_entity_id=$merged_from_id and employee_type='DOCTOR' and status='APPROVED'
and check whether exist at least one employee with same speciality_officio in merged_to_legal_entity
select id from employees where legal_entity_id=$merged_to_id and employee_type='DOCTOR' and status='APPROVED' and party_id=$party_id and speciality.speciality=$speciality.speciality
In case there is no such employee in merged_to_legal_entity change employee's status='DISMISSED' and set `status_reason`='auto_merge_legal_entity' from merged_from legal_entity.
Search declarations
For employees which weren't registered in merged_to legal_entity (employees which status were changed to
DISMISSED during search) find active declarations.
For found declarations by employee_id set
field
value
field
value
status
TERMINATED
reason
auto_reorganization
updated_at
now()
updated_by
$user_id
Update client type for request
Find MITHRIL.clients.id=$merged_from legal_entity.id and update: set MSP_Limited if LE type is MSP or PRIMARY_CARE, EMERGENCY, OUTPATIENT, PHARMACY, MSP_Pharmacy