{"id":50909954,"url":"https://github.com/vs-point/moneys3","last_synced_at":"2026-06-16T09:03:10.098Z","repository":{"id":361864239,"uuid":"1256171183","full_name":"vs-point/moneys3","owner":"vs-point","description":"Striktně typovaná PHP knihovna pro komunikaci s účetním softwarem Money S3 (Seyfor) přes jeho GraphQL API. Poskytuje typovaný PHP interface pro čtení a zápis dokladů, adresáře, skladů, deníků a dalších agend — bez ručního skládání GraphQL řetězců.","archived":false,"fork":false,"pushed_at":"2026-06-01T14:49:42.000Z","size":80,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-01T16:27:09.939Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vs-point.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-01T14:25:57.000Z","updated_at":"2026-06-01T15:01:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vs-point/moneys3","commit_stats":null,"previous_names":["vs-point/moneys3"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/vs-point/moneys3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vs-point%2Fmoneys3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vs-point%2Fmoneys3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vs-point%2Fmoneys3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vs-point%2Fmoneys3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vs-point","download_url":"https://codeload.github.com/vs-point/moneys3/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vs-point%2Fmoneys3/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34398408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-16T09:03:09.292Z","updated_at":"2026-06-16T09:03:10.089Z","avatar_url":"https://github.com/vs-point.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vs-point/moneys3\n\nStriktně typovaná PHP knihovna pro komunikaci s účetním softwarem **Money S3** (Seyfor)\npřes jeho **GraphQL API**. Poskytuje typovaný PHP interface pro čtení a zápis dokladů,\nadresáře, skladů, deníků a dalších agend — bez ručního skládání GraphQL řetězců.\n\n- 🔒 **Striktně typované** — vstupy (mutace) i filtry jsou PHP objekty, ne pole/řetězce.\n- 🧠 **Inteligentní GraphQL builder** — typované objekty se samy serializují do platné\n  GraphQL syntaxe (literály, enum tokeny, vnořené objekty, seznamy).\n- 🔌 **Framework-agnostické jádro** + tenký **Symfony bundle**.\n- ☁️ **Cloud i lokál** — `CloudClient` (`{domain}.api.moneys3.eu`) a `Client` (`localhost:85`).\n\n\u003e Money S3 API je GraphQL-based s OAuth2 autentizací. Čtení je synchronní; **zápis je\n\u003e asynchronní** — doklad vstupuje do importní fronty a mutace vrací `guid` + `isSuccess`.\n\u003e Oficiální dokumentace: \u003chttps://money.cz/navod/api-v-money-s3-pro-vyvojare/\u003e,\n\u003e interaktivní schéma: \u003chttps://s3api.api.moneys3.eu/graphql/\u003e.\n\n## Požadavky\n\n- PHP \u003e= 8.2\n- Money S3 s nainstalovaným a zakoupeným API modulem\n- Vygenerovaný API klíč (Client ID + Client Secret) a Application ID (`api@money.cz`)\n\n## Instalace\n\n```bash\ncomposer config repositories.moneys3 git git@git.vs-point.cz:vspoint/package/moneys3.git\ncomposer require vs-point/moneys3:^1.0\n```\n\nPři použití Dockeru je potřeba GitLab token:\n\n```bash\ncomposer config --auth gitlab-token.git.vs-point.cz \u003ctoken\u003e\n```\n\n## Připojení\n\nKnihovna nabízí dvě konkrétní třídy klienta se společným předkem `MoneyS3Api`, takže si\nexplicitně volíte, ke které instanci se připojujete:\n\n| Třída | Cílová instance | Endpoint |\n|-------|-----------------|----------|\n| `CloudClient` | cloud | `https://{domain}.api.moneys3.eu/graphql/` |\n| `Client` | lokální | `http://localhost:85/graphql/` |\n\n### Cloud\n\n```php\nuse VsPoint\\MoneyS3\\Auth\\Credentials;\nuse VsPoint\\MoneyS3\\Client\\CloudClient;\n\n$client = new CloudClient(\n    domain: 'mojefirma',\n    appId: 'APP-ID-Z-MONEY',\n    credentials: Credentials::clientCredentials('client-id', 'client-secret'),\n);\n```\n\n### Lokální Money S3\n\n```php\nuse VsPoint\\MoneyS3\\Auth\\Credentials;\nuse VsPoint\\MoneyS3\\Client\\Client;\n\n$client = new Client(\n    appId: 'APP-ID-Z-MONEY',\n    credentials: Credentials::clientCredentials('client-id', 'client-secret'),\n    // host: 'localhost', port: 85, secure: false  // výchozí\n);\n```\n\n### Autentizace\n\n```php\n// Client Credentials (z nastavení API klíče)\nCredentials::clientCredentials('client-id', 'client-secret');\n\n// Resource Owner Password Credentials\nCredentials::password('client-id', 'client-secret', 'uzivatel', 'heslo');\n```\n\nToken se získá přes `connect/token`, drží se v paměti a automaticky obnovuje před expirací.\n\n## Použití\n\nKlient přímo vystavuje jednu službu na agendu:\n\n```php\n$client-\u003eissuedInvoices       // faktury vystavené   — R / C / U / D\n$client-\u003ereceivedInvoices     // faktury přijaté      — R / C / U / D\n$client-\u003ebankStatements       // bankovní doklady     — R / C / U / D\n$client-\u003ecashVouchers         // pokladní doklady     — R / C / U / D\n$client-\u003ereceivedOrders       // objednávky přijaté   — R / C / U / D\n$client-\u003eissuedOrders         // objednávky vystavené — R / C / U / D\n$client-\u003ereceivedSlips        // skladové příjemky    — R / C / U / D\n$client-\u003eissuedSlips          // skladové výdejky     — R / C / U / D\n$client-\u003estockTakingDocuments // inventurní doklady   — R / C / U / D\n$client-\u003ewages                // mzdy                 — C / U / D\n$client-\u003ecashJournal          // peněžní deník        — R\n$client-\u003eaccountingJournal    // účetní deník         — R\n$client-\u003ewarehouseStocks      // skladové zásoby      — R\n$client-\u003eagendas              // agendy / firmy       — R\n```\n\n### Čtení (query) — typované DTO\n\n```php\nuse VsPoint\\MoneyS3\\DTO\\Journal\\CashJournalAttribute;\nuse VsPoint\\MoneyS3\\Filter\\FilterOperator;\nuse VsPoint\\MoneyS3\\Filter\\Order;\nuse VsPoint\\MoneyS3\\Filter\\OrderDirection;\nuse VsPoint\\MoneyS3\\Filter\\Where;\n\n// Peněžní deník za rok 2026, řazený podle data sestupně, prvních 20\n$result = $client-\u003ecashJournal-\u003equery(\n    where: Where::field(CashJournalAttribute::year, FilterOperator::eq, 2026),\n    order: Order::by(CashJournalAttribute::date, OrderDirection::desc),\n    take: 20,\n);\n\nforeach ($result as $transaction) {\n    echo $transaction-\u003eid . ' ' . $transaction-\u003eamountHc . ' ' . $transaction-\u003ecompanyName . PHP_EOL;\n}\n```\n\n`$result` je `Collection\u003cJournalTransaction\u003e` — typovaná, iterovatelná, s `first()`,\n`count()`, `isEmpty()`.\n\n### Filtrování (`where`) — typované atributy\n\nPole se **nikdy nezadávají stringem** — odkazují se přes per-agenda atributový enum\nimplementující `FieldName` (např. `CashJournalAttribute`, `IssuedInvoiceAttribute`,\n`WarehouseStockAttribute`). Díky tomu nelze odkázat na pole, které neexistuje:\n\n```php\nWhere::field(CashJournalAttribute::year, FilterOperator::eq, 2026);\nWhere::field(CashJournalAttribute::companyIdentificationNumber, FilterOperator::eq, '01572377'); // vnořené přes tečku\nWhere::and(\n    Where::field(CashJournalAttribute::year, FilterOperator::eq, 2026),\n    Where::field(CashJournalAttribute::amount, FilterOperator::gt, 0),\n);\nWhere::or(/* ... */);\nWhere::allOf([[CashJournalAttribute::year, 2026], [CashJournalAttribute::amount, 100]]); // konjunkce přes `eq`\n```\n\nStejně tak řazení: `Order::by(CashJournalAttribute::date, OrderDirection::desc)`.\n\n\u003e IDE i PHPStan tak hlídají správnost: `CashJournalAttribute::yaer` se nezkompiluje.\n\u003e Názvy polí v enumech jsou navíc **ověřené proti živému schématu** (filter input typy) —\n\u003e viz [Validace proti schématu](#validace-proti-schématu).\n\u003e\n\u003e Pro pole, které zatím nemá atributový enum, existuje únikový poklop\n\u003e `Where::path('nejake.pole', …)` / `Order::path('nejake.pole', …)`.\n\nOperátory (`FilterOperator`): `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`,\n`contains`, `ncontains`, `startsWith`, `nstartsWith`, `endsWith`, `nendsWith`.\n\n### Vlastní výběr polí\n\nRead-only agendy mají výchozí selekci pokrývající celé DTO, ale můžete si zvolit vlastní:\n\n```php\nuse VsPoint\\MoneyS3\\GraphQL\\Field;\n\n$result = $client-\u003ewarehouseStocks-\u003equery(\n    fields: [\n        Field::leaf('description'),\n        Field::leaf('stockQuantity'),\n        Field::nested('warehouse', ['code', 'name']),\n    ],\n    take: 100,\n);\n```\n\nU dokladových agend (faktury, banka, …) je `query()` typově bezpečný „raw\" čteč —\nzvolíte selekci a hodnoty čtete přes `Data`:\n\n```php\n$invoices = $client-\u003eissuedInvoices-\u003equery(\n    fields: [Field::leaf('documentNumber'), Field::leaf('dateOfIssue')],\n    where: Where::field('variableSymbol', FilterOperator::eq, '202603'),\n    take: 5,\n);\n\nforeach ($invoices as $row) {           // $row je VsPoint\\MoneyS3\\Hydration\\Data\n    echo $row-\u003estring('documentNumber') . PHP_EOL;\n}\n```\n\n### Vytvoření dokladu (mutace)\n\n```php\nuse VsPoint\\MoneyS3\\DTO\\Common\\Address;\nuse VsPoint\\MoneyS3\\DTO\\Common\\CodeRef;\nuse VsPoint\\MoneyS3\\DTO\\Common\\DefinitionXMLTransfer;\nuse VsPoint\\MoneyS3\\DTO\\Common\\PartnerAddress;\nuse VsPoint\\MoneyS3\\DTO\\Common\\PostalCode;\nuse VsPoint\\MoneyS3\\DTO\\Common\\ShortCutRef;\nuse VsPoint\\MoneyS3\\DTO\\Common\\VatRateSummary;\nuse VsPoint\\MoneyS3\\DTO\\Invoice\\InvoiceItem;\nuse VsPoint\\MoneyS3\\DTO\\Invoice\\IssuedInvoiceInput;\nuse VsPoint\\MoneyS3\\Enum\\PriceType;\n\n$result = $client-\u003eissuedInvoices-\u003ecreate(\n    new IssuedInvoiceInput(\n        dateOfIssue: '2026-03-02',\n        dateOfTaxing: '2026-03-02',\n        dateOfMaturity: '2026-03-12',\n        documentNumber: '20260203',\n        variableSymbol: '202603',\n        description: 'Popis faktury',\n        accountAssignment: new ShortCutRef('FV001'),\n        vatClassification: new ShortCutRef('19Ř01,02'),\n        payOn: new ShortCutRef('BAN'),\n        vatRateSummaryHc: new VatRateSummary(vatRate: 21, totalWithoutVat: 100, totalVat: 50),\n        partnerAddress: new PartnerAddress(\n            businessAddress: new Address(\n                name: 'Seyfor, a. s.',\n                country: new CodeRef('CZ'),\n                municipality: 'Brno',\n                municipalityPostalCode: new PostalCode('60200'),\n                street: 'Drobného 555/49',\n            ),\n            identificationNumber: '01572377',\n            vatIdentificationNumber: 'CZ01572377',\n        ),\n        paymentMethod: 'převodem',\n        items: [\n            new InvoiceItem(\n                description: 'Vývoj software',\n                amount: 1,\n                unitPriceHc: 100,\n                vatRate: 21,\n                priceType: PriceType::withoutVat,\n            ),\n        ],\n    ),\n    new DefinitionXMLTransfer('_FP+FV'), // definice importu\n);\n\nif ($result-\u003eisSuccess) {\n    echo 'GUID v importní frontě: ' . $result-\u003eguid;\n}\n\n// nebo vyhoďte výjimku, pokud Money S3 doklad nepřijalo:\n$result-\u003eassertSuccess();\n```\n\nDatová pole přijímají `string` (`\"2026-03-02\"`), `DateTimeInterface` i\n`Brick\\DateTime\\LocalDate`. Peněžní/číselné hodnoty přijímají `int|float|string` i\n`Brick\\Math\\BigNumber`.\n\n### Skladová položka faktury\n\n```php\nuse VsPoint\\MoneyS3\\DTO\\Common\\ArticleRef;\nuse VsPoint\\MoneyS3\\DTO\\Common\\StockItem;\nuse VsPoint\\MoneyS3\\DTO\\Common\\WarehouseRef;\nuse VsPoint\\MoneyS3\\DTO\\Invoice\\InvoiceItem;\n\nnew InvoiceItem(\n    description: 'Bota goretex',\n    amount: 1,\n    stockItem: new StockItem(\n        warehouse: new WarehouseRef(code: 'PRODEJ'),\n        article: new ArticleRef(catalogue: 'K50'),\n    ),\n);\n```\n\n### Úprava dokladu\n\nUpdate používá stejné vstupní DTO jako `create`; doklad se identifikuje přes `guid`\n(případně `documentNumber`/symboly podle nastavení importu):\n\n```php\n$client-\u003eissuedInvoices-\u003eupdate(\n    new IssuedInvoiceInput(\n        guid: 'FFBD0FF4-31D0-41C2-9210-823A6DA92A47',\n        description: 'Opravený popis',\n    ),\n);\n```\n\n### Smazání dokladu\n\n```php\n$client-\u003eissuedInvoices-\u003edelete(id: 52, year: 2025);\n$client-\u003ereceivedInvoices-\u003edelete(id: 10, year: 2025);\n$client-\u003ewages-\u003edelete(id: 7); // mzda se maže jen podle id\n```\n\n## Přehled agend a CRUD\n\nStav byl **ověřen proti živému schématu** (`s3api.api.moneys3.eu/graphql/`) — viz\n[Validace proti schématu](#validace-proti-schématu). Zápis je vždy asynchronní (importní\nfronta), mutace vrací `{ guid, isSuccess }`.\n\n| Agenda | Služba | C | R | U | D |\n|--------|--------|---|---|---|---|\n| Faktura vystavená | `issuedInvoices` | ✓ | ✓ | ✓ | ✓ |\n| Faktura přijatá | `receivedInvoices` | ✓ | ✓ | ✓ | ✓ |\n| Bankovní doklad | `bankStatements` | ✓ | ✓ | ✓ | ✓ |\n| Pokladní doklad | `cashVouchers` | ✓ | ✓ | ✓ | ✓ |\n| Objednávka přijatá | `receivedOrders` | ✓ | ✓ | ✓ | ✓ |\n| Objednávka vystavená | `issuedOrders` | ✓ | ✓ | ✓ | ✓ |\n| Skladová příjemka | `receivedSlips` | ✓ | ✓ | ✓ | ✓ |\n| Skladová výdejka | `issuedSlips` | ✓ | ✓ | ✓ | ✓ |\n| Inventurní doklad | `stockTakingDocuments` | ✓ | ✓ | ✓ | ✓ |\n| Mzda | `wages` | ✓ | — | ✓ | ✓ |\n| Peněžní deník | `cashJournal` | — | ✓ | — | — |\n| Účetní deník | `accountingJournal` | — | ✓ | — | — |\n| Skladová zásoba | `warehouseStocks` | — | ✓ | — | — |\n| Agendy / firmy | `agendas` | — | ✓ | — | — |\n\n\u003e Money S3 GraphQL API vystavuje **`create` / `update` / `delete` + query pro desítky\n\u003e dalších agend** (adresář `companies`, kmenové karty `articles`, dodací listy, nabídky,\n\u003e poptávky, interní doklady, závazky/pohledávky, číselníky…). Ty zatím nemají vlastní\n\u003e typované DTO, ale jdou číst generickým `query(fields: […])` patternem a snadno doplnit —\n\u003e viz [Rozšíření o další agendu](#rozšíření-o-další-agendu).\n\n## Validace proti schématu\n\nKnihovna je ověřena proti **živému GraphQL schématu** Money S3 pomocí introspekce\n(`__schema` / `__type` nevyžadují OAuth token). Testovací sada `Schema`\n(`tests/Schema/LiveSchemaContractTest.php`) kontroluje, že:\n\n- všechny query root fieldy (`issuedInvoices`, `journalTrs`, …) ve schématu existují,\n- všechny `create*` / `update*` / `delete*` mutace agend existují,\n- enum tokeny, které knihovna posílá (`PriceType`, `AbsenceType`, řazení `SortEnumType`),\n  jsou platné hodnoty schématu,\n- pole vstupu `IssuedInvoiceInput` odpovídají schématu,\n- názvy polí v atributových enumech (`CashJournalAttribute`, `IssuedInvoiceAttribute`, …)\n  jsou reálná filtrovatelná pole (`I…FilterInput`).\n\nPokud je endpoint nedostupný (offline CI), se test sám přeskočí (`markTestSkipped`), takže\nnikdy nerozbije pipeline. Spuštění jen této sady:\n\n```bash\ndocker run --rm -v \"$(pwd):/app\" -w /app vspoint/php:8.5-fpm-alpine ./vendor/bin/phpunit --testsuite Schema\n```\n\n## Jak funguje typovaný GraphQL builder\n\nJádro je v `src/GraphQL`:\n\n- **`InputObject`** — kontrakt: `toGraphQL(): array` vrací mapu `pole =\u003e hodnota`.\n  Implementují ho všechna vstupní DTO (`IssuedInvoiceInput`, `Address`, `ShortCutRef`, …).\n- **`ValueEncoder`** — serializuje PHP hodnoty do **GraphQL literálů** (ne JSON):\n  řetězce v uvozovkách s escapováním, čísla/bool/null bez uvozovek, **enumy jako holé\n  tokeny** (`WITHOUT_VAT`), `InputObject`/vnořená pole jako `{ key: value }`, seznamy jako\n  `[ … ]`. `null` pole se vynechávají.\n- **`Field`** — uzel selekce (`documentNumber` nebo `company { identificationNumber }`).\n- **`QueryBuilder` / `MutationBuilder`** — složí celý dokument:\n  `query { root(where: …, order: …, skip: …, take: …) { items { …fields } } }`,\n  resp. `mutation { createX(x: { … }, definitionXMLTransfer: { … }) { guid isSuccess } }`.\n- **`Where` / `Order`** (`src/Filter`) — kompozičně skládané, typované argumenty.\n\nEnumy, které vyžadují jiný token než název case, implementují `GraphQLEnumValue`. Pro\nojedinělý surový fragment existuje `RawGraphQL` (používat střídmě).\n\n## Symfony bundle\n\nJádro je framework-agnostické; bundle jen registruje a autowiruje připojení.\n\n```php\n// config/bundles.php\nreturn [\n    // ...\n    VsPoint\\MoneyS3\\Bridge\\Symfony\\MoneyS3Bundle::class =\u003e ['all' =\u003e true],\n];\n```\n\n```yaml\n# config/packages/money_s3.yaml\nmoney_s3:\n    connection: cloud            # cloud | local\n    app_id: '%env(MONEYS3_APP_ID)%'\n    cloud:\n        domain: '%env(MONEYS3_DOMAIN)%'\n    # local:\n    #     host: localhost\n    #     port: 85\n    #     secure: false\n    auth:\n        grant_type: client_credentials   # client_credentials | password\n        client_id: '%env(MONEYS3_CLIENT_ID)%'\n        client_secret: '%env(MONEYS3_CLIENT_SECRET)%'\n        # username: '%env(MONEYS3_USERNAME)%'\n        # password: '%env(MONEYS3_PASSWORD)%'\n```\n\n```php\nuse VsPoint\\MoneyS3\\Client\\MoneyS3Api;\n\nfinal class InvoiceImporter\n{\n    public function __construct(private readonly MoneyS3Api $money) {}\n\n    public function run(): void\n    {\n        $this-\u003emoney-\u003eissuedInvoices-\u003ecreate(/* ... */);\n    }\n}\n```\n\nAutowiruje se `MoneyS3Api` i konkrétní třída (`CloudClient` / `Client`) podle `connection`.\n\n## Rozšíření o další agendu\n\n1. **Atributový enum** v `src/DTO/{Agenda}/{Agenda}Attribute.php` implementující `FieldName`\n   (mapuje PHP název → reálné GraphQL filtrovatelné pole, vnořené přes tečku) — pro typované\n   `Where` / `Order`.\n2. **Vstupní DTO** v `src/DTO/{Agenda}/` implementující `InputObject` (jen `toGraphQL()`\n   s `pole =\u003e hodnota`; `null` pole se automaticky vynechají).\n3. **Response DTO** (u read agend) s `public static fromData(Data $d): self` a\n   `public static fields(): array` (selekce `Field[]`).\n4. **Služba** v `src/Agenda/` rozšiřující `AbstractAgendaService` — `query()` přes\n   `queryCollection()` / `queryRaw()`, `create()` / `update()` / `delete()` přes `mutate()`.\n5. Přidat `public readonly` property + inicializaci do `src/Client/MoneyS3Api.php`.\n\nPřesné názvy root fieldů, mutací, vstupních polí a enum tokenů ověřte v interaktivním\nschématu: \u003chttps://s3api.api.moneys3.eu/graphql/\u003e („Browse Schema\").\n\n## Vývoj\n\nVše běží přes Docker image z CI (`vspoint/php:8.5-fpm-alpine`):\n\n```bash\n# Instalace závislostí\ndocker run --rm -v \"$(pwd):/app\" -w /app vspoint/php:8.5-fpm-alpine composer install\n\n# Testy\ndocker run --rm -v \"$(pwd):/app\" -w /app vspoint/php:8.5-fpm-alpine ./vendor/bin/phpunit --testdox\n\n# Statická analýza (PHPStan level 6)\ndocker run --rm -v \"$(pwd):/app\" -w /app vspoint/php:8.5-fpm-alpine ./vendor/bin/phpstan analyse\n\n# Code style (ECS) — kontrola / oprava\ndocker run --rm -v \"$(pwd):/app\" -w /app vspoint/php:8.5-fpm-alpine ./vendor/bin/ecs check\ndocker run --rm -v \"$(pwd):/app\" -w /app vspoint/php:8.5-fpm-alpine ./vendor/bin/ecs check --fix\n```\n\nTesty jsou **unit** — neprovádějí žádné síťové volání (HTTP je mockované přes Guzzle\n`MockHandler`, transport přes `RecordingTransport`). Ověřují sestavování GraphQL dokumentů,\nserializaci typovaných vstupů, parsování odpovědí, OAuth2 a resolving endpointů.\n\nCI (`.gitlab-ci.yml`) má fáze `analyse` (validate + phpstan + ecs) a `test` (phpunit).\n\n## Licence\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvs-point%2Fmoneys3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvs-point%2Fmoneys3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvs-point%2Fmoneys3/lists"}