Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Table of Contents

Purpose

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:

legalEntities.graphql

legalEntityMergeJobs.graphql

legal entity must be registered in the new one.  After that NHS admin must set relationship via signed content. After basic validation

Specification

Schemas:

Expand
titlelegalEntities.graphql
Code Block
languagejson
"""
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
}
Page Properties
idAPI_Specification

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

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

    1. Verify the validity of access token

      1. in case of error return 401 ('Access denied')

    2. Check user scope legal_entity:merge in order to perform this action

      1. 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. 

  1. Get client_id from token. Find prm.legal_entities by client_id

    1. Check EDRPOU matches prm.legal_entities.EDRPOU

      1. Check if  EDRPOU in Certificate details exists and not empty

        1. Check if  Certificate_details.EDRPOU=prm.legal_entities.EDRPOU

      2. In case validation fails - generate 422 error

  2. Get parties.tax_id using party_users.party_id by user_id.

    1. Compare DRFO in Certificate with party.tax_id

      1. Convert DRFO and TAX_ID to uppercase

      2. Compare DRFO and TAX_ID as Cyrillic letters

      3. Convert DRFO to Cyrillic and compare as Cyrillic letters

    2. In case validation fails - generate 422 error

Verify role

Extract from token:

  1. Validate client_id (is_blocked=false)

    1. in case of error return 403 Error ('Client is blocked')

  2. Check client_id is active

    • in case error return 403 - ('Client is not active')

Validate request

  1. Validate merged_to legal_entity

    1. Check legal entity exists and status='active' 

      1. in case of error return 409  error view $merged_to.id ('Merged to legal entity must be active')

    2. 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

      1. in case of error return 409  error view $merged_to.id ('Merged to legal entity is in the process of reorganisation itself')

    3. Validate merged_to.name from request=prm.legal_entites.$merged_to.id.name

      1. in case of error return 422  error view $merged_to.name ('Invalid legal entity name')

    4. Validate merged_to.edrpou from request=prm.legal_entites.$merged_to.id.edrpou

      1. in case of error return 422  error view $merged_to.edrpou ('Invalid legal entity edrpou')

  2. Validate merged_from legal_entity

    1. Check merged from legal entity exists and status='active' or 'suspended'

      1. in case of error return 409  error view $merged_from.id ('Merged from legal entity must be active or suspended')

    2. 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

      1. in case of error return 409  error view $merged_from.id ('Merged from legal entity is already in the process of reorganisation')

    3. Validate merged from legal_entity.name from request=prm.legal_entites.$merged_from.id.name

      1. in case of error return 422  error view $merged_from.name ('Invalid legal entity name')

    4. Validate merged from legal_entity.edrpou from request=prm.legal_entites.$merged_from.id.edrpou

      1. in case of error return 422  error view $merged_from.edrpou ('Invalid legal entity edrpou')

    5. Validate merged_to_legal_entity.id<>merged_from_legal_entity.id

      1. in case of error return 422  error view ('Legator and successor legal entities must be different')

  3. Validate merged_to.legal_entity_type and merged_from.legal_entity_type. Valid type transitions see in the table below.

    1. in case of error return 422  error view ('Invalid legal entity type')

Valid type transitions for legal entities

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 

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

  1. Get url for request upload.

    Parameter
    Source
    action'GET'
    bucket'RELATED_LEGAL_ENTITIES'
    resource_id: RELATED_LEGAL_ENTITIES_ID
    resource_name: CREATE_RELATED_LEGAL_ENTITIES
    timestamp:TIMESTAMP
  2. Upload signed content to media storage


Search employees (employee_type='DOCTOR')

  1. 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

    1. 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 

...

  1. 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

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

field

value

update_at

now()

client_type_id

$MSP_LIMITED_ID(3770c4b3-05cd-42d9-8e15-233b193aee86)

Update merged_from legal entity status

Set merged_from legal entity status to 'reorganised'

Save request

Insert record to PRM.RELATED_LEGAL_ENTITIES

field

value

id

generate id

merged_from_id

$merged_from.id

merged_to_id

$merged_to.id

is_active

true

reason

$reason

inserted_at

now()

inserted_by

$user_id

Response structure

In response can be received validation error or the job will be started.

...