| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- <?php
-
- declare(strict_types=1);
-
- namespace App\Service;
-
- use App\Domain\Entity\ParsedPortfolio;
- use App\Domain\Entity\PortfolioDetailItem;
- use App\Domain\Entity\PortfolioHeader;
- use App\Domain\Entity\PortfolioMovementItem;
-
- class XmlParser
- {
- private const XML_HEADER_AGRMT = 'ШапкаГенСог';
- private const XML_HEADER_PERIOD = 'ШапкаПериод';
- private const XML_PORTFOLIO_TABLE = 'ШапкаПортфель';
- private const XML_PORTFOLIO_SECTION = 'ЗаголовокПортфель';
- private const XML_MOVEMENTS_SECTION = 'ШапкаДвиженияИОстаткиЦБ';
- private const PREFIX_DETAILS = 'Детали';
-
- private const ATTR_CLIENT = 'Клиент';
- private const ATTR_START_DATE = 'НачПериода';
- private const ATTR_END_DATE = 'КонПериода';
- private const ATTR_PERIOD_LINE = 'СтрокаПериода';
- private const ATTR_PERIOD = 'Период';
- private const ATTR_SECURITY = 'ЦБ';
- private const ATTR_ISSUER = 'Эмитент';
- private const ATTR_PRICE_START = 'НОЦена';
- private const ATTR_PRICE_END = 'КОЦена';
- private const ATTR_QTY_START = 'КоличествоНО';
- private const ATTR_QTY_END = 'КоличествоКО';
- private const ATTR_SUM_START = 'СуммаНКДНО';
- private const ATTR_SUM_END = 'СуммаНКДКО';
- private const ATTR_SUM_TOTAL = 'КОСумма';
- private const ATTR_QTY_START_MV = 'НО';
- private const ATTR_QTY_END_MV = 'КО';
- private const ATTR_QTY_INCOME = 'Приход';
- private const ATTR_QTY_OUTCOME = 'Расход';
- private const DATE_FORMAT = 'd.m.Y';
-
- public function processXml(\SimpleXMLElement $xml): ParsedPortfolio
- {
- return new ParsedPortfolio(
- header: $this->processXmlHeader($xml),
- details: $this->processDetails(
- $xml->{self::XML_PORTFOLIO_SECTION}->{self::XML_PORTFOLIO_TABLE}
- ),
- movements: $this->processMovements(
- $xml->{self::XML_MOVEMENTS_SECTION}
- ),
- );
- }
-
- private function processXmlHeader(\SimpleXMLElement $xml): PortfolioHeader
- {
- $attrs = [];
- foreach ([self::XML_HEADER_AGRMT, self::XML_HEADER_PERIOD] as $section) {
- foreach ($xml->{$section}->attributes() as $key => $value) {
- $attrs[$key] = get_object_vars($value)[0];
- }
- }
-
- if (array_key_exists(self::ATTR_START_DATE, $attrs)) {
- $startDate = \DateTimeImmutable::createFromFormat(
- self::DATE_FORMAT,
- $attrs[self::ATTR_START_DATE],
- );
- $endDate = \DateTimeImmutable::createFromFormat(
- self::DATE_FORMAT,
- $attrs[self::ATTR_END_DATE],
- );
- } elseif (array_key_exists(self::ATTR_PERIOD_LINE, $attrs)) {
- $chunks = explode(' ', $attrs[self::ATTR_PERIOD_LINE]);
- if (count($chunks) !== 4) {
- throw new \RuntimeException('Unexpected period format');
- }
- $startDate = \DateTimeImmutable::createFromFormat(self::DATE_FORMAT, $chunks[1]);
- $endDate = \DateTimeImmutable::createFromFormat(self::DATE_FORMAT, $chunks[3]);
- } else {
- throw new \RuntimeException('Cannot detect XML header version');
- }
-
- if (!$startDate || !$endDate) {
- throw new \RuntimeException('Invalid date format in XML header');
- }
-
- return new PortfolioHeader(
- clientAgreement: $attrs[self::ATTR_CLIENT],
- startDate: $startDate,
- endDate: $endDate,
- );
- }
-
- private function processDetails(\SimpleXMLElement $xml): array
- {
- $items = [];
-
- foreach (get_object_vars($xml) as $key => $value) {
- if (mb_strpos($key, self::PREFIX_DETAILS) === false) {
- continue;
- }
-
- if (!is_array($value)) {
- $value = [$value];
- }
-
- foreach ($value as $line) {
- $attr = [];
- foreach ($line->attributes() as $attrName => $attrValue) {
- $attr[$attrName] = get_object_vars($attrValue)[0];
- }
-
- $items[] = new PortfolioDetailItem(
- issuer: $attr[self::ATTR_ISSUER],
- security: $attr[self::ATTR_SECURITY],
- priceStart: $attr[self::ATTR_PRICE_START],
- priceEnd: $attr[self::ATTR_PRICE_END],
- quantityStart: (int) $attr[self::ATTR_QTY_START],
- quantityEnd: (int) $attr[self::ATTR_QTY_END],
- sumStart: $attr[self::ATTR_SUM_START],
- sumEnd: $attr[self::ATTR_SUM_END],
- sumTotal: $attr[self::ATTR_SUM_TOTAL],
- );
- }
- }
-
- return $items;
- }
-
- private function processMovements(\SimpleXMLElement $xml): array
- {
- $items = [];
-
- foreach (get_object_vars($xml) as $key => $value) {
- if (mb_strpos($key, self::PREFIX_DETAILS) === false) {
- continue;
- }
-
- if (!is_array($value)) {
- $value = [$value];
- }
-
- foreach ($value as $line) {
- $attr = [];
- foreach ($line->attributes() as $attrName => $attrValue) {
- $attr[$attrName] = get_object_vars($attrValue)[0];
- }
-
- $items[] = new PortfolioMovementItem(
- security: $attr[self::ATTR_SECURITY],
- period: new \DateTimeImmutable($attr[self::ATTR_PERIOD]),
- quantityStart: (int) $attr[self::ATTR_QTY_START_MV],
- quantityEnd: (int) $attr[self::ATTR_QTY_END_MV],
- quantityIncome: (int) $attr[self::ATTR_QTY_INCOME],
- quantityOutcome:(int) $attr[self::ATTR_QTY_OUTCOME],
- );
- }
- }
-
- return $items;
- }
- }
|