├── .env ├── .env.test ├── .gitignore ├── .php-cs-fixer.dist.php ├── README.md ├── bin └── console ├── composer.json ├── composer.lock ├── config ├── bundles.php ├── packages │ ├── cache.yaml │ ├── doctrine.yaml │ ├── doctrine_migrations.yaml │ ├── features_flag.yaml │ ├── framework.yaml │ ├── prod │ │ └── doctrine.yaml │ ├── routing.yaml │ └── test │ │ ├── dama_doctrine_test_bundle.yaml │ │ └── doctrine.yaml ├── preload.php ├── routes.yaml ├── routes │ ├── annotations.yaml │ └── framework.yaml ├── services.yaml └── services_test.yaml ├── docker-compose.override.yml ├── docker-compose.yml ├── docs └── xstate.js ├── migrations ├── Version20220121160323.php ├── Version20220123120851.php ├── Version20220123224330.php ├── Version20220209194742.php ├── Version20220210152256.php ├── Version20220210184450.php ├── Version20220211204206.php ├── Version20220212082639.php ├── Version20220212164138.php ├── Version20220303185743.php ├── Version20220326082730.php ├── Version20220327203954.php ├── Version20220407185153.php ├── Version20220414204404.php ├── Version20220418190006.php ├── Version20220418223109.php ├── Version20220419063121.php ├── Version20220419070117.php └── Version20220420061415.php ├── phpunit.xml.dist ├── public └── index.php ├── src ├── Agreements │ ├── Contract.php │ ├── ContractAttachment.php │ ├── ContractAttachmentDTO.php │ ├── ContractAttachmentData.php │ ├── ContractAttachmentDataRepository.php │ ├── ContractController.php │ ├── ContractDTO.php │ ├── ContractRepository.php │ └── ContractService.php ├── Assignment │ ├── AssignmentStatus.php │ ├── DriverAssignment.php │ ├── DriverAssignmentFacade.php │ ├── DriverAssignmentRepository.php │ └── InvolvedDriversSummary.php ├── CarFleet │ ├── CarType.php │ ├── CarTypeActiveCounter.php │ ├── CarTypeActiveCounterRepository.php │ ├── CarTypeController.php │ ├── CarTypeDTO.php │ ├── CarTypeRepository.php │ └── CarTypeService.php ├── Common │ ├── BaseEntity.php │ ├── Clock.php │ ├── DTOArgumentResolver.php │ ├── ObjectHash.php │ ├── RequestTransactionSubscriber.php │ └── SystemClock.php ├── Config │ ├── AppProperties.php │ └── Neo4jClientFactory.php ├── Contracts │ ├── Application │ │ ├── Acme │ │ │ ├── Dynamic │ │ │ │ ├── DocumentOperationResult.php │ │ │ │ └── DocumentResourceManager.php │ │ │ └── Straigthforward │ │ │ │ ├── AcmeContractProcessBasedOnStraightforwardDocumentModel.php │ │ │ │ ├── AcmeStateFactory.php │ │ │ │ └── ContractResult.php │ │ └── Editor │ │ │ ├── CommitResult.php │ │ │ ├── DocumentDTO.php │ │ │ └── DocumentEditor.php │ ├── Infra │ │ └── DoctrineDocumentHeaderRepository.php │ ├── Legacy │ │ ├── BaseAggregateRoot.php │ │ ├── Contract2.php │ │ ├── Document.php │ │ ├── DocumentStatus.php │ │ ├── OOParadigm.php │ │ ├── Printable.php │ │ ├── UnsupportedTransitionException.php │ │ ├── User.php │ │ ├── UserRepository.php │ │ └── Versionable.php │ └── Model │ │ ├── Content │ │ ├── ContentVersion.php │ │ ├── DocumentContent.php │ │ ├── DocumentContentRepository.php │ │ └── DocumentNumber.php │ │ ├── ContentId.php │ │ ├── DocumentHeader.php │ │ ├── DocumentHeaderRepository.php │ │ └── State │ │ ├── Dynamic │ │ ├── Acme │ │ │ └── AcmeContractStateAssembler.php │ │ ├── ChangeCommand.php │ │ ├── Config │ │ │ ├── Actions │ │ │ │ ├── Action.php │ │ │ │ ├── ChangeVerifier.php │ │ │ │ └── PublishEvent.php │ │ │ ├── Events │ │ │ │ ├── DocumentEvent.php │ │ │ │ ├── DocumentPublished.php │ │ │ │ └── DocumentUnpublished.php │ │ │ └── Predicates │ │ │ │ ├── ContentChange │ │ │ │ ├── NegativePredicate.php │ │ │ │ ├── PositivePredicate.php │ │ │ │ └── Predicate.php │ │ │ │ └── StateChange │ │ │ │ ├── AuthorIsNotAVerifier.php │ │ │ │ ├── ContentNotEmptyVerifier.php │ │ │ │ ├── PositiveVerifier.php │ │ │ │ ├── Predicate.php │ │ │ │ └── PreviousStateVerifier.php │ │ ├── State.php │ │ ├── StateBuilder.php │ │ └── StateConfig.php │ │ └── Straightforward │ │ ├── Acme │ │ ├── ArchivedState.php │ │ ├── DraftState.php │ │ ├── PublishedState.php │ │ └── VerifiedState.php │ │ └── BaseState.php ├── Crm │ ├── Claims │ │ ├── Claim.php │ │ ├── ClaimAttachment.php │ │ ├── ClaimAttachmentRepository.php │ │ ├── ClaimController.php │ │ ├── ClaimDTO.php │ │ ├── ClaimNumberGenerator.php │ │ ├── ClaimRepository.php │ │ ├── ClaimResolver │ │ │ └── Result.php │ │ ├── ClaimService.php │ │ ├── ClaimsResolver.php │ │ └── ClaimsResolverRepository.php │ ├── Client.php │ ├── ClientController.php │ ├── ClientDTO.php │ ├── ClientRepository.php │ ├── ClientService.php │ └── TransitAnalyzer │ │ ├── AnalyzedAddressesDTO.php │ │ ├── GraphTransitAnalyzer.php │ │ ├── PopulateGraphService.php │ │ └── TransitAnalyzerController.php ├── DriverFleet │ ├── Driver.php │ ├── DriverAttribute.php │ ├── DriverAttributeDTO.php │ ├── DriverAttributeRepository.php │ ├── DriverController.php │ ├── DriverDTO.php │ ├── DriverFee.php │ ├── DriverFeeRepository.php │ ├── DriverFeeService.php │ ├── DriverLicense.php │ ├── DriverReport │ │ ├── DriverReport.php │ │ ├── DriverReportController.php │ │ ├── SqlBasedDriverReportCreator.php │ │ └── TravelledDistance │ │ │ ├── TimeSlot.php │ │ │ ├── TravelledDistance.php │ │ │ ├── TravelledDistanceRepository.php │ │ │ └── TravelledDistanceService.php │ ├── DriverRepository.php │ └── DriverService.php ├── Geolocation │ ├── Address │ │ ├── Address.php │ │ ├── AddressDTO.php │ │ └── AddressRepository.php │ ├── Distance.php │ ├── DistanceCalculator.php │ ├── DistanceType.php │ └── GeocodingService.php ├── Invocing │ ├── Invoice.php │ ├── InvoiceGenerator.php │ └── InvoiceRepository.php ├── Kernel.php ├── Loyalty │ ├── AwardedMiles.php │ ├── AwardsAccount.php │ ├── AwardsAccountController.php │ ├── AwardsAccountDTO.php │ ├── AwardsAccountRepository.php │ ├── AwardsService.php │ ├── AwardsServiceImpl.php │ ├── ConstantUntil.php │ ├── Miles.php │ ├── MilesType.php │ └── TwoStepExpiringMiles.php ├── Money │ ├── Money.php │ └── MoneyType.php ├── Notification │ ├── ClientNotificationService.php │ └── DriverNotificationService.php ├── Party │ ├── Api │ │ ├── PartyId.php │ │ ├── PartyMapper.php │ │ └── RoleObjectFactory.php │ ├── Infra │ │ ├── DoctrinePartyRelationshipRepository.php │ │ └── DoctrinePartyRepository.php │ └── Model │ │ ├── Party │ │ ├── Party.php │ │ ├── PartyRelationship.php │ │ ├── PartyRelationshipRepository.php │ │ ├── PartyRepository.php │ │ └── PartyRole.php │ │ └── Role │ │ └── PartyBasedRole.php ├── Pricing │ ├── Tariff.php │ └── Tariffs.php ├── Repair │ ├── Api │ │ ├── AssistanceRequest.php │ │ ├── ContractManager.php │ │ ├── RepairProcess.php │ │ ├── RepairRequest.php │ │ └── ResolveResult.php │ ├── Legacy │ │ ├── DAO │ │ │ └── UserDAO.php │ │ ├── Job │ │ │ ├── CommonBaseAbstractJob.php │ │ │ ├── JobResult.php │ │ │ ├── MaintenanceJob.php │ │ │ └── RepairJob.php │ │ ├── Parts │ │ │ └── Parts.php │ │ ├── Service │ │ │ └── JobDoer.php │ │ └── User │ │ │ ├── CommonBaseAbstractUser.php │ │ │ ├── EmployeeDriver.php │ │ │ ├── EmployeeDriverWithLeasedCar.php │ │ │ ├── EmployeeDriverWithOwnCar.php │ │ │ ├── SignedContract.php │ │ │ ├── SubcontractorDriver.php │ │ │ ├── SubcontractorDriverWithOwnCar.php │ │ │ └── SubcontractorWithRentedCar.php │ └── Model │ │ ├── Dict │ │ ├── PartyRelationshipsDictionary.php │ │ └── PartyRolesDictionary.php │ │ └── Roles │ │ ├── Assistance │ │ └── RoleForAssistance.php │ │ ├── Blank │ │ ├── Customer.php │ │ └── Insured.php │ │ └── Repair │ │ ├── ExtendedInsurance.php │ │ ├── RepairingResult.php │ │ ├── RoleForRepairer.php │ │ └── Warranty.php ├── Ride │ ├── ChangeDestinationService.php │ ├── ChangePickupService.php │ ├── CompleteTransitService.php │ ├── DemandService.php │ ├── Details │ │ ├── Status.php │ │ ├── TransitDetails.php │ │ ├── TransitDetailsDTO.php │ │ ├── TransitDetailsFacade.php │ │ └── TransitDetailsRepository.php │ ├── Events │ │ └── TransitCompleted.php │ ├── RequestForTransit.php │ ├── RequestForTransitRepository.php │ ├── RequestTransitService.php │ ├── RideService.php │ ├── StartTransitService.php │ ├── Transit.php │ ├── TransitController.php │ ├── TransitDTO.php │ ├── TransitDemand.php │ ├── TransitDemandRepository.php │ └── TransitRepository.php └── Tracking │ ├── DriverPosition.php │ ├── DriverPositionDTO.php │ ├── DriverPositionDTOV2.php │ ├── DriverPositionRepository.php │ ├── DriverSession.php │ ├── DriverSessionController.php │ ├── DriverSessionDTO.php │ ├── DriverSessionRepository.php │ ├── DriverSessionService.php │ ├── DriverTrackingController.php │ └── DriverTrackingService.php ├── symfony.lock └── tests ├── Common ├── AddressFixture.php ├── AwardsAccountFixture.php ├── CarTypeFixture.php ├── ClaimFixture.php ├── ClientFixture.php ├── DriverFixture.php ├── Factory.php ├── FixedClock.php ├── Fixtures.php ├── PrivateProperty.php ├── RideFixture.php ├── StubbedTransitPrice.php └── TransitFixture.php ├── Double ├── FakeAppProperties.php ├── FakeGeocodingService.php └── FakeTariffs.php ├── Integration ├── AnalyzeNearbyTransitsIntegrationTest.php ├── AwardMilesManagementIntegrationTest.php ├── CalculateDriverFeeIntegrationTest.php ├── CalculateDriverPeriodicPaymentsIntegrationTest.php ├── CalculateDriverTravelledDistanceIntegrationTest.php ├── CarTypeUpdateIntegrationTest.php ├── ClaimAutomaticResolvingIntegrationTest.php ├── ContractLifecycleIntegrationTest.php ├── Contracts │ └── Application │ │ └── Acme │ │ ├── Dynamic │ │ ├── AcmeContractManagerBasedOnDynamicStateModelTest.php │ │ └── DocumentOperationResultAssert.php │ │ └── Straightforward │ │ ├── AcmeContractProcessBasedOnStraightforwardStateModelTest.php │ │ └── ContractResultAssert.php ├── CreateDriverReportIntegrationTest.php ├── DriverTrackingServiceIntegrationTest.php ├── ExpiringMilesIntegrationTest.php ├── GraphTransitAnalyzerIntegrationTest.php ├── Neo4jTestCase.php ├── PopulateGraphServiceIntegrationTest.php ├── RemovingAwardMilesIntegrationTest.php ├── Repair │ ├── RepairProcessTest.php │ └── VehicleRepairAssert.php ├── TariffRecognizingIntegrationTest.php ├── TransitLifeCycleIntegrationTest.php └── ValidateDriverLicenseIntegrationTest.php ├── Unit ├── Agreements │ └── ContractLifecycleTest.php ├── Assigment │ └── DriverAssignmentTest.php ├── Contracts │ ├── Legacy │ │ └── DocumentTest.php │ └── Model │ │ └── State │ │ ├── Dynamic │ │ ├── AcmeContractTest.php │ │ └── FakeDocumentPublisher.php │ │ └── Straightforward │ │ └── AcmeContractTest.php ├── Distance │ └── DistanceTest.php ├── Entity │ ├── ClaimAutomaticResolvingTest.php │ ├── DriverLicenseTest.php │ ├── Miles │ │ └── MilesTest.php │ ├── TariffTest.php │ └── TimeSlotTest.php ├── Money │ └── MoneyTest.php ├── Pricing │ └── CalculateTransitPriceTest.php ├── Repair │ └── Legacy │ │ ├── Job │ │ └── RepairTest.php │ │ └── Service │ │ └── JobDoerTest.php ├── Ride │ ├── RequestForTransitTest.php │ ├── TransitDemandTest.php │ └── TransitTest.php └── Ui │ └── CalculateTransitDistanceTest.php └── bootstrap.php /.env: -------------------------------------------------------------------------------- 1 | # In all environments, the following files are loaded if they exist, 2 | # the latter taking precedence over the former: 3 | # 4 | # * .env contains default values for the environment variables needed by the app 5 | # * .env.local uncommitted file with local overrides 6 | # * .env.$APP_ENV committed environment-specific defaults 7 | # * .env.$APP_ENV.local uncommitted environment-specific overrides 8 | # 9 | # Real environment variables win over .env files. 10 | # 11 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. 12 | # 13 | # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). 14 | # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration 15 | 16 | ###> symfony/framework-bundle ### 17 | APP_ENV=dev 18 | APP_SECRET=d7bcf0b5664f320f82dc7b620705ff0f 19 | ###< symfony/framework-bundle ### 20 | 21 | ###> doctrine/doctrine-bundle ### 22 | # Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 23 | # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml 24 | # 25 | # DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" 26 | # DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7" 27 | DATABASE_URL="postgresql://symfony:ChangeMe@127.0.0.1:5432/app?serverVersion=13&charset=utf8" 28 | ###< doctrine/doctrine-bundle ### 29 | 30 | NEO4J_DSN="bolt://neo4j:password@localhost" 31 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | KERNEL_CLASS='LegacyFighter\Cabs\Kernel' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ###> symfony/framework-bundle ### 2 | /.env.local 3 | /.env.local.php 4 | /.env.*.local 5 | /config/secrets/prod/prod.decrypt.private.php 6 | /public/bundles/ 7 | /var/ 8 | /vendor/ 9 | ###< symfony/framework-bundle ### 10 | 11 | ###> friendsofphp/php-cs-fixer ### 12 | /.php-cs-fixer.php 13 | /.php-cs-fixer.cache 14 | ###< friendsofphp/php-cs-fixer ### 15 | 16 | /.phpunit.cache 17 | /phpunit.xml 18 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in(__DIR__.'/src') 5 | ->in(__DIR__.'/tests') 6 | ; 7 | 8 | return (new PhpCsFixer\Config()) 9 | ->setRiskyAllowed(true) 10 | ->setRules([ 11 | '@PHP71Migration' => true, 12 | '@Symfony' => true, 13 | 'array_syntax' => ['syntax' => 'short'], 14 | 'no_superfluous_elseif' => true, 15 | 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true], 16 | 'no_unreachable_default_argument_value' => true, 17 | 'no_useless_else' => true, 18 | 'no_useless_return' => true, 19 | 'no_unused_imports' => true, 20 | 'declare_strict_types' => true, 21 | 'ordered_imports' => [ 22 | 'imports_order' => null, 23 | 'sort_algorithm' => 'alpha', 24 | ], 25 | 'phpdoc_order' => true, 26 | 'phpdoc_align' => true, 27 | 'phpdoc_no_access' => true, 28 | 'phpdoc_separation' => true, 29 | 'increment_style' => true, 30 | 'single_quote' => true, 31 | 'trim_array_spaces' => true, 32 | 'single_blank_line_before_namespace' => true, 33 | 'yoda_style' => false, 34 | 'global_namespace_import' => [ 35 | 'import_classes' => false, 36 | 'import_constants' => false, 37 | 'import_functions' => false 38 | ], 39 | // risky --> 40 | 'strict_param' => true, 41 | ]) 42 | ->setFinder($finder) 43 | ; 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rozwój kodu 2 | 3 | Kod będzie rozwijać się wraz z cotygodniową narracją szkoleniową. 4 | Zarówno pojawiać się w nim będą kolejne poprawki jak i odziedziczone po firmach partnerskich nowe moduły ;-) Jak to w prawdziwym legacy. 5 | 6 | # Przeglądanie kodu 7 | 8 | Poszczególne kroki refaktoryzacyjne najlepiej przeglądać używając tagów. Każdy krok szkoleniowy, który opisany jest w odcinku Legacy Fighter posiada na końcu planszę z nazwą odpowiedniego taga. Porównać zmiany można robiąc diffa w stosunku do poprzedniego taga z narracji. 9 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | ['all' => true], 5 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 6 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 7 | Pheature\Community\Symfony\PheatureFlagsBundle::class => ['all' => true], 8 | DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], 9 | ]; 10 | -------------------------------------------------------------------------------- /config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Unique name of your app: used to compute stable namespaces for cache keys. 4 | #prefix_seed: your_vendor_name/app_name 5 | 6 | # The "app" cache stores to the filesystem by default. 7 | # The data in this cache should persist between deploys. 8 | # Other options include: 9 | 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | 14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 15 | #app: cache.adapter.apcu 16 | 17 | # Namespaced pools use the above "app" backend by default 18 | #pools: 19 | #my.dedicated.cache: null 20 | -------------------------------------------------------------------------------- /config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | url: '%env(resolve:DATABASE_URL)%' 4 | types: 5 | money: 'LegacyFighter\Cabs\Money\MoneyType' 6 | miles: 'LegacyFighter\Cabs\Loyalty\MilesType' 7 | distance: 'LegacyFighter\Cabs\Geolocation\DistanceType' 8 | # IMPORTANT: You MUST configure your server version, 9 | # either here or in the DATABASE_URL env var (see .env file) 10 | #server_version: '13' 11 | orm: 12 | auto_generate_proxy_classes: true 13 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 14 | auto_mapping: true 15 | mappings: 16 | LegacyFighter: 17 | is_bundle: false 18 | dir: '%kernel.project_dir%/src' 19 | prefix: 'LegacyFighter\Cabs' 20 | alias: LegacyFighter 21 | -------------------------------------------------------------------------------- /config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | migrations_paths: 3 | # namespace is arbitrary but should be different from App\Migrations 4 | # as migrations classes should NOT be autoloaded 5 | 'DoctrineMigrations': '%kernel.project_dir%/migrations' 6 | enable_profiler: '%kernel.debug%' 7 | -------------------------------------------------------------------------------- /config/packages/features_flag.yaml: -------------------------------------------------------------------------------- 1 | pheature_flags: 2 | driver: "inmemory" 3 | driver_options: [] 4 | toggles: [] 5 | -------------------------------------------------------------------------------- /config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | # see https://symfony.com/doc/current/reference/configuration/framework.html 2 | framework: 3 | secret: '%env(APP_SECRET)%' 4 | #csrf_protection: true 5 | http_method_override: false 6 | 7 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 8 | # Remove or comment this section to explicitly disable session support. 9 | session: 10 | handler_id: null 11 | cookie_secure: auto 12 | cookie_samesite: lax 13 | storage_factory_id: session.storage.factory.native 14 | 15 | #esi: true 16 | #fragments: true 17 | php_errors: 18 | log: true 19 | 20 | when@test: 21 | framework: 22 | test: true 23 | session: 24 | storage_factory_id: session.storage.factory.mock_file 25 | -------------------------------------------------------------------------------- /config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | auto_generate_proxy_classes: false 4 | query_cache_driver: 5 | type: pool 6 | pool: doctrine.system_cache_pool 7 | result_cache_driver: 8 | type: pool 9 | pool: doctrine.result_cache_pool 10 | 11 | framework: 12 | cache: 13 | pools: 14 | doctrine.result_cache_pool: 15 | adapter: cache.app 16 | doctrine.system_cache_pool: 17 | adapter: cache.system 18 | -------------------------------------------------------------------------------- /config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | utf8: true 4 | 5 | # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. 6 | # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands 7 | #default_uri: http://localhost 8 | 9 | when@prod: 10 | framework: 11 | router: 12 | strict_requirements: null 13 | -------------------------------------------------------------------------------- /config/packages/test/dama_doctrine_test_bundle.yaml: -------------------------------------------------------------------------------- 1 | dama_doctrine_test: 2 | enable_static_connection: true 3 | enable_static_meta_data_cache: true 4 | enable_static_query_cache: true 5 | -------------------------------------------------------------------------------- /config/packages/test/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | # "TEST_TOKEN" is typically set by ParaTest 4 | dbname_suffix: '_test%env(default::TEST_TOKEN)%' 5 | -------------------------------------------------------------------------------- /config/preload.php: -------------------------------------------------------------------------------- 1 | doctrine/doctrine-bundle ### 5 | database: 6 | ports: 7 | - "5432" 8 | ###< doctrine/doctrine-bundle ### 9 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | ###> doctrine/doctrine-bundle ### 5 | database: 6 | image: postgres:${POSTGRES_VERSION:-13}-alpine 7 | environment: 8 | POSTGRES_DB: ${POSTGRES_DB:-app} 9 | # You should definitely change the password in production 10 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe} 11 | POSTGRES_USER: ${POSTGRES_USER:-symfony} 12 | volumes: 13 | - db-data:/var/lib/postgresql/data:rw 14 | # You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! 15 | # - ./docker/db/data:/var/lib/postgresql/data:rw 16 | ###< doctrine/doctrine-bundle ### 17 | 18 | neo4j: 19 | image: neo4j:4.0.3 20 | hostname: neo4j 21 | container_name: neo4j 22 | ports: 23 | - "7474:7474" 24 | - "7687:7687" 25 | volumes: 26 | - ./neo4j/plugins:/plugins 27 | environment: 28 | NEO4J_AUTH: neo4j/password 29 | NEO4J_dbms_logs_debug_level: DEBUG 30 | 31 | volumes: 32 | ###> doctrine/doctrine-bundle ### 33 | db-data: 34 | ###< doctrine/doctrine-bundle ### 35 | -------------------------------------------------------------------------------- /docs/xstate.js: -------------------------------------------------------------------------------- 1 | // https://xstate.js.org/viz/ 2 | 3 | // Available variables: 4 | // - Machine 5 | // - interpret 6 | // - assign 7 | // - send 8 | // - sendParent 9 | // - spawn 10 | // - raise 11 | // - actions 12 | // - XState (all XState exports) 13 | 14 | const machine = Machine({ 15 | id: 'transit', 16 | initial: 'draft', 17 | context: { 18 | retries: 0 19 | }, 20 | states: { 21 | draft: { 22 | on: { 23 | PUBLISH: 'waiting_for_driver_acceptance' 24 | } 25 | }, 26 | waiting_for_driver_acceptance: { 27 | on: { 28 | CANCEL: 'cancelled', 29 | ACCEPT: 'transit_to_passenger', 30 | FAILED: 'driver_assignment_failed' 31 | } 32 | }, 33 | transit_to_passenger: { 34 | on: { 35 | CANCEL: 'cancelled', 36 | START_TRANSIT: 'in_transit' 37 | } 38 | }, 39 | in_transit: { 40 | on: { 41 | COMPLETE: 'completed' 42 | } 43 | }, 44 | cancelled: { 45 | type: 'final' 46 | }, 47 | completed: { 48 | type: 'final' 49 | }, 50 | driver_assignment_failed: { 51 | type: 'final' 52 | } 53 | } 54 | }); 55 | -------------------------------------------------------------------------------- /migrations/Version20220123120851.php: -------------------------------------------------------------------------------- 1 | addSql('COMMENT ON COLUMN driver_fee.min IS \'(DC2Type:money)\''); 24 | $this->addSql('COMMENT ON COLUMN transit.price IS \'(DC2Type:money)\''); 25 | $this->addSql('COMMENT ON COLUMN transit.estimated_price IS \'(DC2Type:money)\''); 26 | } 27 | 28 | public function down(Schema $schema): void 29 | { 30 | // this down() migration is auto-generated, please modify it to your needs 31 | $this->addSql('COMMENT ON COLUMN transit.price IS NULL'); 32 | $this->addSql('COMMENT ON COLUMN transit.estimated_price IS NULL'); 33 | $this->addSql('COMMENT ON COLUMN driver_fee.min IS NULL'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /migrations/Version20220123224330.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE transit ADD tariff_km_rate DOUBLE PRECISION NOT NULL'); 24 | $this->addSql('ALTER TABLE transit ADD tariff_name VARCHAR(255) NOT NULL'); 25 | $this->addSql('ALTER TABLE transit ADD tariff_base_fee INT NOT NULL'); 26 | $this->addSql('ALTER TABLE transit DROP factor'); 27 | } 28 | 29 | public function down(Schema $schema): void 30 | { 31 | // this down() migration is auto-generated, please modify it to your needs 32 | $this->addSql('ALTER TABLE transit ADD factor INT DEFAULT NULL'); 33 | $this->addSql('ALTER TABLE transit DROP tariff_km_rate'); 34 | $this->addSql('ALTER TABLE transit DROP tariff_name'); 35 | $this->addSql('ALTER TABLE transit DROP tariff_base_fee'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /migrations/Version20220209194742.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE car_type_active_counter (car_class VARCHAR(255) NOT NULL, active_cars_counter INT NOT NULL, PRIMARY KEY(car_class))'); 24 | $this->addSql('ALTER TABLE car_type DROP active_cars_counter'); 25 | $this->addSql('ALTER TABLE transit ALTER drivers_fee TYPE INT'); 26 | $this->addSql('ALTER TABLE transit ALTER drivers_fee DROP DEFAULT'); 27 | $this->addSql('COMMENT ON COLUMN transit.drivers_fee IS \'(DC2Type:money)\''); 28 | } 29 | 30 | public function down(Schema $schema): void 31 | { 32 | // this down() migration is auto-generated, please modify it to your needs 33 | $this->addSql('DROP TABLE car_type_active_counter'); 34 | $this->addSql('ALTER TABLE car_type ADD active_cars_counter INT NOT NULL'); 35 | $this->addSql('ALTER TABLE transit ALTER drivers_fee TYPE INT'); 36 | $this->addSql('ALTER TABLE transit ALTER drivers_fee DROP DEFAULT'); 37 | $this->addSql('COMMENT ON COLUMN transit.drivers_fee IS NULL'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /migrations/Version20220210152256.php: -------------------------------------------------------------------------------- 1 | addSql('DROP INDEX uniq_a769de27617c70e0'); 24 | $this->addSql('CREATE INDEX IDX_A769DE27617C70E0 ON claim (transit_id)'); 25 | } 26 | 27 | public function down(Schema $schema): void 28 | { 29 | // this down() migration is auto-generated, please modify it to your needs 30 | $this->addSql('DROP INDEX IDX_A769DE27617C70E0'); 31 | $this->addSql('CREATE UNIQUE INDEX uniq_a769de27617c70e0 ON claim (transit_id)'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /migrations/Version20220210184450.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE SEQUENCE claims_resolver_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); 24 | $this->addSql('CREATE TABLE claims_resolver (id INT NOT NULL, client_id INT NOT NULL, claimed_transits_ids JSON NOT NULL, version INT DEFAULT 1 NOT NULL, PRIMARY KEY(id))'); 25 | } 26 | 27 | public function down(Schema $schema): void 28 | { 29 | // this down() migration is auto-generated, please modify it to your needs 30 | $this->addSql('DROP SEQUENCE claims_resolver_id_seq CASCADE'); 31 | $this->addSql('DROP TABLE claims_resolver'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /migrations/Version20220211204206.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE awarded_miles DROP expiration_date'); 24 | $this->addSql('ALTER TABLE awarded_miles DROP is_special'); 25 | $this->addSql('ALTER TABLE awarded_miles DROP miles'); 26 | $this->addSql('ALTER TABLE awarded_miles ADD miles JSON NOT NULL'); 27 | } 28 | 29 | public function down(Schema $schema): void 30 | { 31 | // this down() migration is auto-generated, please modify it to your needs 32 | $this->addSql('ALTER TABLE awarded_miles ADD expiration_date TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); 33 | $this->addSql('ALTER TABLE awarded_miles ADD is_special BOOLEAN NOT NULL'); 34 | $this->addSql('ALTER TABLE awarded_miles DROP miles'); 35 | $this->addSql('ALTER TABLE awarded_miles ADD miles INT NOT NULL'); 36 | $this->addSql('COMMENT ON COLUMN awarded_miles.expiration_date IS \'(DC2Type:datetime_immutable)\''); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /migrations/Version20220212082639.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE awarded_miles ADD account_id INT DEFAULT NULL'); 24 | $this->addSql('ALTER TABLE awarded_miles ADD CONSTRAINT FK_43471B899B6B5FBA FOREIGN KEY (account_id) REFERENCES awards_account (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); 25 | $this->addSql('CREATE INDEX IDX_43471B899B6B5FBA ON awarded_miles (account_id)'); 26 | $this->addSql('COMMENT ON COLUMN awarded_miles.miles IS \'(DC2Type:miles)\''); 27 | } 28 | 29 | public function down(Schema $schema): void 30 | { 31 | // this down() migration is auto-generated, please modify it to your needs 32 | $this->addSql('ALTER TABLE awarded_miles DROP CONSTRAINT FK_43471B899B6B5FBA'); 33 | $this->addSql('DROP INDEX IDX_43471B899B6B5FBA'); 34 | $this->addSql('ALTER TABLE awarded_miles DROP account_id'); 35 | $this->addSql('COMMENT ON COLUMN awarded_miles.miles IS NULL'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /migrations/Version20220303185743.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE travelled_distance (interval_id UUID NOT NULL, driver_id INT NOT NULL, last_latitude DOUBLE PRECISION NOT NULL, last_longitude DOUBLE PRECISION NOT NULL, distance DOUBLE PRECISION NOT NULL, beginning TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, "end" TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(interval_id))'); 24 | $this->addSql('COMMENT ON COLUMN travelled_distance.interval_id IS \'(DC2Type:uuid)\''); 25 | $this->addSql('COMMENT ON COLUMN travelled_distance.distance IS \'(DC2Type:distance)\''); 26 | $this->addSql('COMMENT ON COLUMN travelled_distance.beginning IS \'(DC2Type:datetime_immutable)\''); 27 | $this->addSql('COMMENT ON COLUMN travelled_distance."end" IS \'(DC2Type:datetime_immutable)\''); 28 | } 29 | 30 | public function down(Schema $schema): void 31 | { 32 | // this down() migration is auto-generated, please modify it to your needs 33 | $this->addSql('DROP TABLE travelled_distance'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /migrations/Version20220407185153.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE SEQUENCE signed_contract_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); 24 | $this->addSql('CREATE TABLE signed_contract (id INT NOT NULL, covered_parts JSON NOT NULL, coverage_ratio DOUBLE PRECISION NOT NULL, version INT DEFAULT 1 NOT NULL, PRIMARY KEY(id))'); 25 | } 26 | 27 | public function down(Schema $schema): void 28 | { 29 | // this down() migration is auto-generated, please modify it to your needs 30 | $this->addSql('DROP SEQUENCE signed_contract_id_seq CASCADE'); 31 | $this->addSql('DROP TABLE signed_contract'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /migrations/Version20220419063121.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE driver_session DROP CONSTRAINT fk_15bbb1b4c3423909'); 24 | $this->addSql('DROP INDEX idx_15bbb1b4c3423909'); 25 | $this->addSql('ALTER TABLE driver_session ALTER driver_id SET NOT NULL'); 26 | } 27 | 28 | public function down(Schema $schema): void 29 | { 30 | // this down() migration is auto-generated, please modify it to your needs 31 | $this->addSql('ALTER TABLE driver_session ALTER driver_id DROP NOT NULL'); 32 | $this->addSql('ALTER TABLE driver_session ADD CONSTRAINT fk_15bbb1b4c3423909 FOREIGN KEY (driver_id) REFERENCES driver (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); 33 | $this->addSql('CREATE INDEX idx_15bbb1b4c3423909 ON driver_session (driver_id)'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | tests 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | src 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | contractAttachmentNo = $contractAttachmentId; 25 | $this->data = $data; 26 | $this->creationDate = new \DateTimeImmutable(); 27 | } 28 | 29 | public function getContractAttachmentNo(): Uuid 30 | { 31 | return $this->contractAttachmentNo; 32 | } 33 | 34 | public function getData(): string 35 | { 36 | return $this->data; 37 | } 38 | 39 | public function getCreationDate(): \DateTimeImmutable 40 | { 41 | return $this->creationDate; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Agreements/ContractAttachmentDataRepository.php: -------------------------------------------------------------------------------- 1 | em->persist($contractAttachmentData); 16 | $this->em->flush(); 17 | return $contractAttachmentData; 18 | } 19 | 20 | /** 21 | * @param Uuid[] $attachmentIds 22 | * @return ContractAttachmentData[] 23 | */ 24 | public function findByContractAttachmentNoIn(array $attachmentIds): array 25 | { 26 | return $this->em->getRepository(ContractAttachmentData::class)->matching(Criteria::create()->where( 27 | Criteria::expr()->in('contractAttachmentNo', $attachmentIds) 28 | ))->toArray(); 29 | } 30 | 31 | public function deleteByAttachmentId(int $id): void 32 | { 33 | $this->em->getConnection()->executeQuery(' 34 | DELETE FROM contract_attachment_data cad WHERE cad.contract_attachment_no = ( 35 | (SELECT ca.contract_attachment_no FROM contract_attachment ca WHERE ca.id = :id) 36 | )', ['id' => $id]); 37 | $this->em->clear(ContractAttachmentData::class); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Agreements/ContractRepository.php: -------------------------------------------------------------------------------- 1 | em->getRepository(Contract::class)->findBy(['partnerName' => $partnerName]); 18 | } 19 | 20 | public function save(Contract $contract): Contract 21 | { 22 | $this->em->persist($contract); 23 | $this->em->flush(); 24 | return $contract; 25 | } 26 | 27 | public function getOne(int $id): ?Contract 28 | { 29 | return $this->em->find(Contract::class, $id); 30 | } 31 | 32 | public function findByAttachmentId(int $attachmentId): ?Contract 33 | { 34 | return $this->em->createQuery(sprintf( 35 | 'SELECT c FROM %s c JOIN c.attachments ca WHERE ca.id = %s', 36 | Contract::class, 37 | $attachmentId 38 | ))->getSingleResult(); 39 | } 40 | 41 | public function findContractAttachmentNoById(int $attachmentId): ?Uuid 42 | { 43 | $uuid = $this->em->getConnection()->fetchOne( 44 | 'SELECT c.contract_attachment_no FROM contract_attachment c WHERE c.id = :id', 45 | ['id' => $attachmentId] 46 | ); 47 | return $uuid !== false ? Uuid::fromString($uuid) : null; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Assignment/AssignmentStatus.php: -------------------------------------------------------------------------------- 1 | em->persist($driverAssignment); 15 | $this->em->flush(); 16 | return $driverAssignment; 17 | } 18 | 19 | public function findByRequestUuid(Uuid $requestUuid): ?DriverAssignment 20 | { 21 | return $this->em->getRepository(DriverAssignment::class)->findOneBy([ 22 | 'requestId' => $requestUuid 23 | ]); 24 | } 25 | 26 | public function findByRequestUuidAndStatus(Uuid $requestId, string $status): ?DriverAssignment 27 | { 28 | return $this->em->getRepository(DriverAssignment::class)->findOneBy([ 29 | 'requestId' => $requestId, 30 | 'status' => $status 31 | ]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Assignment/InvolvedDriversSummary.php: -------------------------------------------------------------------------------- 1 | carClass = $carClass; 22 | } 23 | 24 | public function getActiveCarsCounter(): int 25 | { 26 | return $this->activeCarsCounter; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/CarFleet/CarTypeActiveCounterRepository.php: -------------------------------------------------------------------------------- 1 | em->find(CarTypeActiveCounter::class, $carClass); 14 | } 15 | 16 | public function save(CarTypeActiveCounter $carTypeActiveCounter): void 17 | { 18 | $this->em->persist($carTypeActiveCounter); 19 | } 20 | 21 | public function incrementCounter(string $carClass): void 22 | { 23 | $this->em->getConnection()->executeQuery(' 24 | UPDATE car_type_active_counter counter SET active_cars_counter = active_cars_counter + 1 25 | WHERE counter.car_class = :car_class', [ 26 | 'car_class' => $carClass 27 | ]); 28 | $this->em->clear(CarTypeActiveCounter::class); 29 | } 30 | 31 | public function decrementCounter(string $carClass): void 32 | { 33 | $this->em->getConnection()->executeQuery(' 34 | UPDATE car_type_active_counter counter SET active_cars_counter = active_cars_counter - 1 35 | WHERE counter.car_class = :car_class', [ 36 | 'car_class' => $carClass 37 | ]); 38 | $this->em->clear(CarTypeActiveCounter::class); 39 | } 40 | 41 | public function delete(CarTypeActiveCounter $carTypeActiveCounter): void 42 | { 43 | $this->em->remove($carTypeActiveCounter); 44 | $this->em->flush(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/CarFleet/CarTypeController.php: -------------------------------------------------------------------------------- 1 | carTypeService->create($carTypeDTO)); 17 | } 18 | 19 | #[Route('/cartypes/{carClass}/registerCar', methods: ['POST'])] 20 | public function registerCar(string $carClass): Response 21 | { 22 | $this->carTypeService->registerCar($carClass); 23 | return new JsonResponse(); 24 | } 25 | 26 | #[Route('/cartypes/{carClass}/unregisterCar', methods: ['POST'])] 27 | public function unregisterCar(string $carClass): Response 28 | { 29 | $this->carTypeService->unregisterCar($carClass); 30 | return new JsonResponse(); 31 | } 32 | 33 | #[Route('/cartypes/{id}/activate', methods: ['POST'])] 34 | public function activate(int $id): Response 35 | { 36 | $this->carTypeService->activate($id); 37 | return new JsonResponse(); 38 | } 39 | 40 | #[Route('/cartypes/{id}/deactivate', methods: ['POST'])] 41 | public function deactivate(int $id): Response 42 | { 43 | $this->carTypeService->deactivate($id); 44 | return new JsonResponse(); 45 | } 46 | 47 | #[Route('/cartypes/{id}', methods: ['GET'])] 48 | public function find(int $id): Response 49 | { 50 | return new JsonResponse($this->carTypeService->loadDto($id)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/CarFleet/CarTypeRepository.php: -------------------------------------------------------------------------------- 1 | em->find(CarType::class, $carTypeId); 18 | } 19 | 20 | public function findByCarClass(string $carClass): ?CarType 21 | { 22 | return $this->em->getRepository(CarType::class)->findOneBy(['carClass' => $carClass]); 23 | } 24 | 25 | public function findActiveCounter(string $carClass): ?CarTypeActiveCounter 26 | { 27 | return $this->carTypeActiveCounterRepository->findByCarClass($carClass); 28 | } 29 | 30 | /** 31 | * @return CarType[] 32 | */ 33 | public function findByStatus(string $status): array 34 | { 35 | return $this->em->getRepository(CarType::class)->findBy(['status' => $status]); 36 | } 37 | 38 | public function save(CarType $carType): void 39 | { 40 | $this->em->persist($carType); 41 | $this->carTypeActiveCounterRepository->save(new CarTypeActiveCounter($carType->getCarClass())); 42 | $this->em->flush(); 43 | } 44 | 45 | public function delete(CarType $carType): void 46 | { 47 | $this->em->remove($carType); 48 | $this->carTypeActiveCounterRepository->delete($this->carTypeActiveCounterRepository->findByCarClass($carType->getCarClass())); 49 | $this->em->flush(); 50 | } 51 | 52 | public function incrementCounter(string $carClass): void 53 | { 54 | $this->carTypeActiveCounterRepository->incrementCounter($carClass); 55 | } 56 | 57 | public function decrementCounter(string $carClass): void 58 | { 59 | $this->carTypeActiveCounterRepository->decrementCounter($carClass); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Common/BaseEntity.php: -------------------------------------------------------------------------------- 1 | id; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Common/Clock.php: -------------------------------------------------------------------------------- 1 | serializer = new Serializer( 23 | [new PropertyNormalizer(null, null, new ReflectionExtractor())], 24 | [new JsonEncoder()] 25 | ); 26 | } 27 | 28 | 29 | public function supports(Request $request, ArgumentMetadata $argument) 30 | { 31 | return str_starts_with($argument->getType(), 'LegacyFighter\Cabs\DTO'); 32 | } 33 | 34 | public function resolve(Request $request, ArgumentMetadata $argument) 35 | { 36 | try { 37 | yield $this->serializer->deserialize($request->getContent(), $argument->getType(), 'json'); 38 | } catch (ExceptionInterface $exception) { 39 | throw new BadRequestHttpException($exception->getMessage(), $exception); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Common/ObjectHash.php: -------------------------------------------------------------------------------- 1 | entityManager = $entityManager; 18 | } 19 | 20 | public static function getSubscribedEvents(): array 21 | { 22 | return [ 23 | KernelEvents::CONTROLLER => ['startTransaction', 10], 24 | KernelEvents::RESPONSE => ['commitTransaction', 10], 25 | KernelEvents::EXCEPTION => ['rollbackTransaction', 11], 26 | ]; 27 | } 28 | 29 | public function startTransaction(KernelEvent $event): void 30 | { 31 | if(in_array($event->getRequest()->getMethod(), [Request::METHOD_POST, Request::METHOD_DELETE], true)) { 32 | $this->entityManager->beginTransaction(); 33 | } 34 | } 35 | 36 | public function commitTransaction(): void 37 | { 38 | if($this->entityManager->getConnection()->isTransactionActive()) { 39 | $this->entityManager->flush(); 40 | $this->entityManager->commit(); 41 | } 42 | } 43 | 44 | public function rollbackTransaction(): void 45 | { 46 | if($this->entityManager->getConnection()->isTransactionActive()) { 47 | $this->entityManager->rollback(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Common/SystemClock.php: -------------------------------------------------------------------------------- 1 | noOfTransitsForClaimAutomaticRefund; 17 | } 18 | 19 | public function getAutomaticRefundForVipThreshold(): int 20 | { 21 | return $this->automaticRefundForVipThreshold; 22 | } 23 | 24 | public function getMinNoOfCarsForEcoClass(): int 25 | { 26 | return $this->minNoOfCarsForEcoClass; 27 | } 28 | 29 | public function getMilesExpirationInDays(): int 30 | { 31 | return $this->milesExpirationInDays; 32 | } 33 | 34 | public function getDefaultMilesBonus(): int 35 | { 36 | return $this->defaultMilesBonus; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Config/Neo4jClientFactory.php: -------------------------------------------------------------------------------- 1 | withDriver('bolt', $neo4jDsn) 14 | ->withDefaultDriver('bolt') 15 | ->build(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Contracts/Application/Acme/Dynamic/DocumentOperationResult.php: -------------------------------------------------------------------------------- 1 | result; 29 | } 30 | 31 | public function getStateName(): string 32 | { 33 | return $this->stateName; 34 | } 35 | 36 | public function getContentId(): ?ContentId 37 | { 38 | return $this->contentId; 39 | } 40 | 41 | public function getDocumentHeaderId(): int 42 | { 43 | return $this->documentHeaderId; 44 | } 45 | 46 | public function getDocumentNumber(): DocumentNumber 47 | { 48 | return $this->documentNumber; 49 | } 50 | 51 | public function getPossibleTransitionsAndRules(): array 52 | { 53 | return $this->possibleTransitionsAndRules; 54 | } 55 | 56 | public function isContentChangePossible(): bool 57 | { 58 | return $this->contentChangePossible; 59 | } 60 | 61 | public function getContentChangePredicate(): string 62 | { 63 | return $this->contentChangePredicate; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Contracts/Application/Acme/Straigthforward/AcmeStateFactory.php: -------------------------------------------------------------------------------- 1 | getStateDescriptor(); 16 | if($className === null) { 17 | $state = new DraftState(); 18 | $state->init($header); 19 | return $state; 20 | } 21 | 22 | try { 23 | $state = new $className(); 24 | $state->init($header); 25 | return $state; 26 | } catch (\Throwable $exception) { 27 | throw new \RuntimeException('Invalid class name '.$className, 0, $exception); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Contracts/Application/Acme/Straigthforward/ContractResult.php: -------------------------------------------------------------------------------- 1 | result; 24 | } 25 | 26 | public function getDocumentHeaderId(): int 27 | { 28 | return $this->documentHeaderId; 29 | } 30 | 31 | public function getDocumentNumber(): DocumentNumber 32 | { 33 | return $this->documentNumber; 34 | } 35 | 36 | public function getStateDescriptor(): ?string 37 | { 38 | return $this->stateDescriptor; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Contracts/Application/Editor/CommitResult.php: -------------------------------------------------------------------------------- 1 | contentId = $contentId; 19 | $this->result = $result; 20 | $this->message = $message; 21 | } 22 | 23 | public function getContentId(): Uuid 24 | { 25 | return $this->contentId; 26 | } 27 | 28 | public function getResult(): string 29 | { 30 | return $this->result; 31 | } 32 | 33 | public function getMessage(): ?string 34 | { 35 | return $this->message; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Contracts/Application/Editor/DocumentDTO.php: -------------------------------------------------------------------------------- 1 | contentId = $contentId; 17 | $this->physicalContent = $physicalContent; 18 | $this->contentVersion = $contentVersion; 19 | } 20 | 21 | public function getContentId(): ?Uuid 22 | { 23 | return $this->contentId; 24 | } 25 | 26 | public function getPhysicalContent(): string 27 | { 28 | return $this->physicalContent; 29 | } 30 | 31 | public function getContentVersion(): ContentVersion 32 | { 33 | return $this->contentVersion; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Contracts/Application/Editor/DocumentEditor.php: -------------------------------------------------------------------------------- 1 | getContentId(); 20 | $content = new DocumentContent($previousID, $document->getContentVersion(), $document->getPhysicalContent()); 21 | $this->documentContentRepository->save($content); 22 | return new CommitResult($content->getId(), CommitResult::SUCCESS); 23 | } 24 | 25 | public function get(Uuid $contentId): DocumentDTO 26 | { 27 | $content = $this->documentContentRepository->getOne($contentId); 28 | return new DocumentDTO($contentId, $content->getPhysicalContent(), $content->getContentVersion()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Contracts/Infra/DoctrineDocumentHeaderRepository.php: -------------------------------------------------------------------------------- 1 | em->contains($header)) { 20 | $this->em->lock($header, LockMode::OPTIMISTIC); 21 | } else { 22 | $this->em->persist($header); 23 | } 24 | $this->em->flush(); 25 | } 26 | 27 | public function getOne(int $id): ?DocumentHeader 28 | { 29 | assert($this->em instanceof EntityManager); 30 | return $this->em->find(DocumentHeader::class, $id, LockMode::OPTIMISTIC); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Contracts/Legacy/BaseAggregateRoot.php: -------------------------------------------------------------------------------- 1 | status, DocumentStatus::PUBLISHED); 10 | } 11 | 12 | public function accept(): void 13 | { 14 | if($this->status === DocumentStatus::VERIFIED) { 15 | $this->status = DocumentStatus::PUBLISHED; //reusing unused enum to provide data model for new status 16 | } 17 | } 18 | 19 | //Contracts just don't have a title, it's just a part of the content 20 | public function changeTitle(string $title): void 21 | { 22 | parent::changeContent($title . $this->getContent()); 23 | } 24 | 25 | public function changeContentFor(string $content, string $userStatus): void 26 | { 27 | if($userStatus === 'ChiefSalesOfficerStatus' || $this->misterVladimirIsLoggedIn($userStatus)) { 28 | $this->overridePublished = true; 29 | $this->changeContent($content); 30 | } 31 | } 32 | 33 | private function misterVladimirIsLoggedIn(string $userStatus): bool 34 | { 35 | return trim(mb_strtolower($userStatus)) === '!!!id='.self::NUMBER_OF_THE_BEAST; 36 | } 37 | 38 | private const NUMBER_OF_THE_BEAST = 616; 39 | 40 | public function recreateTo(int $version): void 41 | { 42 | //TODO need to learn Kafka 43 | } 44 | 45 | public function getLastVersion(): int 46 | { 47 | return random_int(1, PHP_INT_MAX); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/Contracts/Legacy/DocumentStatus.php: -------------------------------------------------------------------------------- 1 | current; 18 | } 19 | 20 | public function getDesired(): string 21 | { 22 | return $this->desired; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Contracts/Legacy/User.php: -------------------------------------------------------------------------------- 1 | em->persist($user); 14 | $this->em->flush(); 15 | return $user; 16 | } 17 | 18 | public function getOne(int $userId): ?User 19 | { 20 | return $this->em->find(User::class, $userId); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Contracts/Legacy/Versionable.php: -------------------------------------------------------------------------------- 1 | id = Uuid::v4(); 28 | } 29 | 30 | public function getId(): Uuid 31 | { 32 | return $this->id; 33 | } 34 | 35 | public function getPreviousId(): ?Uuid 36 | { 37 | return $this->previousId; 38 | } 39 | 40 | public function getContentVersion(): ContentVersion 41 | { 42 | return $this->contentVersion; 43 | } 44 | 45 | public function getPhysicalContent(): string 46 | { 47 | return $this->physicalContent; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Contracts/Model/Content/DocumentContentRepository.php: -------------------------------------------------------------------------------- 1 | em->persist($content); 15 | $this->em->flush(); 16 | } 17 | 18 | public function getOne(Uuid $contentId): DocumentContent 19 | { 20 | $documentContent = $this->em->find(DocumentContent::class, $contentId); 21 | if($documentContent===null) { 22 | throw new \RuntimeException(sprintf('Document %s not found', $contentId)); 23 | } 24 | 25 | return $documentContent; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Contracts/Model/Content/DocumentNumber.php: -------------------------------------------------------------------------------- 1 | contentId = $contentId; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Contracts/Model/DocumentHeader.php: -------------------------------------------------------------------------------- 1 | authorId = $authorId; 21 | $this->number = $number; 22 | } 23 | 24 | public function changeCurrentContent(ContentId $contentId): void 25 | { 26 | $this->contentId = $contentId; 27 | } 28 | 29 | public function noEmpty(): bool 30 | { 31 | return $this->contentId !== null; 32 | } 33 | 34 | public function getAuthorId(): int 35 | { 36 | return $this->authorId; 37 | } 38 | 39 | public function setAuthorId(int $authorId): void 40 | { 41 | $this->authorId = $authorId; 42 | } 43 | 44 | public function getVerifierId(): ?int 45 | { 46 | return $this->verifierId; 47 | } 48 | 49 | public function setVerifierId(?int $verifierId): void 50 | { 51 | $this->verifierId = $verifierId; 52 | } 53 | 54 | public function getStateDescriptor(): ?string 55 | { 56 | return $this->stateDescriptor; 57 | } 58 | 59 | public function setStateDescriptor(?string $stateDescriptor): void 60 | { 61 | $this->stateDescriptor = $stateDescriptor; 62 | } 63 | 64 | public function getDocumentNumber(): DocumentNumber 65 | { 66 | return $this->number; 67 | } 68 | 69 | public function getContentId(): ?ContentId 70 | { 71 | return $this->contentId; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Contracts/Model/DocumentHeaderRepository.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | private array $params; 12 | 13 | public function __construct(string $desiredState, array $params = []) 14 | { 15 | $this->desiredState = $desiredState; 16 | $this->params = $params; 17 | } 18 | 19 | public function getDesiredState(): string 20 | { 21 | return $this->desiredState; 22 | } 23 | 24 | public function getParam(string $name) 25 | { 26 | return $this->params[$name] ?? null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Contracts/Model/State/Dynamic/Config/Actions/Action.php: -------------------------------------------------------------------------------- 1 | setVerifierId($changeCommand->getParam(self::PARAM_VERIFIER)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Contracts/Model/State/Dynamic/Config/Actions/PublishEvent.php: -------------------------------------------------------------------------------- 1 | $eventClass 14 | */ 15 | public function __construct( 16 | private string $eventClass, 17 | private EventDispatcherInterface $dispatcher 18 | ) 19 | { 20 | } 21 | 22 | public function apply(DocumentHeader $documentHeader, ChangeCommand $changeCommand): void 23 | { 24 | $eventClass = $this->eventClass; 25 | $this->dispatcher->dispatch(new $eventClass( 26 | $documentHeader->getId(), 27 | $documentHeader->getStateDescriptor(), 28 | $documentHeader->getContentId(), 29 | $documentHeader->getDocumentNumber() 30 | )); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/Contracts/Model/State/Dynamic/Config/Events/DocumentEvent.php: -------------------------------------------------------------------------------- 1 | documentId = $documentId; 18 | $this->currentSate = $currentSate; 19 | $this->contentId = $contentId; 20 | $this->documentNumber = $documentNumber; 21 | } 22 | 23 | public function getDocumentId(): int 24 | { 25 | return $this->documentId; 26 | } 27 | 28 | public function getCurrentSate(): string 29 | { 30 | return $this->currentSate; 31 | } 32 | 33 | public function getContentId(): ContentId 34 | { 35 | return $this->contentId; 36 | } 37 | 38 | public function getDocumentNumber(): DocumentNumber 39 | { 40 | return $this->documentNumber; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Contracts/Model/State/Dynamic/Config/Events/DocumentPublished.php: -------------------------------------------------------------------------------- 1 | getParam(ChangeVerifier::PARAM_VERIFIER) !== $state->getDocumentHeader()->getAuthorId(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Contracts/Model/State/Dynamic/Config/Predicates/StateChange/ContentNotEmptyVerifier.php: -------------------------------------------------------------------------------- 1 | getDocumentHeader()->getContentId() !== null; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Contracts/Model/State/Dynamic/Config/Predicates/StateChange/PositiveVerifier.php: -------------------------------------------------------------------------------- 1 | getStateDescriptor() === $this->stateDescriptor; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Contracts/Model/State/Dynamic/StateConfig.php: -------------------------------------------------------------------------------- 1 | getDocumentHeader()->noEmpty(); 23 | } 24 | 25 | protected function acquire(DocumentHeader $header): void 26 | { 27 | 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/Contracts/Model/State/Straightforward/Acme/VerifiedState.php: -------------------------------------------------------------------------------- 1 | verifierId = $verifierId; 15 | } 16 | 17 | protected function canChangeContent(): bool 18 | { 19 | return true; 20 | } 21 | 22 | protected function stateAfterContentChange(): BaseState 23 | { 24 | return new DraftState(); 25 | } 26 | 27 | protected function canChangeFrom(BaseState $previousState): bool 28 | { 29 | return $previousState instanceof DraftState 30 | && $previousState->getDocumentHeader()->getAuthorId() !== $this->verifierId 31 | && $previousState->getDocumentHeader()->noEmpty() 32 | ; 33 | } 34 | 35 | protected function acquire(DocumentHeader $header): void 36 | { 37 | $this->documentHeader->setVerifierId($this->verifierId); 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/Crm/Claims/ClaimAttachment.php: -------------------------------------------------------------------------------- 1 | creationDate = new \DateTimeImmutable(); 28 | } 29 | 30 | public function getClientId(): int 31 | { 32 | return $this->claim->getOwnerId(); 33 | } 34 | 35 | public function getClaim(): Claim 36 | { 37 | return $this->claim; 38 | } 39 | 40 | public function getCreationDate(): \DateTimeImmutable 41 | { 42 | return $this->creationDate; 43 | } 44 | 45 | public function setCreationDate(\DateTimeImmutable $creationDate): void 46 | { 47 | $this->creationDate = $creationDate; 48 | } 49 | 50 | public function getDescription(): string 51 | { 52 | return $this->description; 53 | } 54 | 55 | public function setDescription(string $description): void 56 | { 57 | $this->description = $description; 58 | } 59 | 60 | public function getData(): string 61 | { 62 | return $this->data; 63 | } 64 | 65 | public function setData(string $data): void 66 | { 67 | $this->data = $data; 68 | } 69 | 70 | public function setClaim(Claim $claim): void 71 | { 72 | $this->claim = $claim; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Crm/Claims/ClaimAttachmentRepository.php: -------------------------------------------------------------------------------- 1 | em->persist($claimAttachment); 14 | $this->em->flush(); 15 | } 16 | 17 | public function getOne(int $id): ?ClaimAttachment 18 | { 19 | return $this->em->find(ClaimAttachment::class, $id); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Crm/Claims/ClaimController.php: -------------------------------------------------------------------------------- 1 | claimService->create($claimDTO); 17 | return new JsonResponse(ClaimDTO::from($created)); 18 | } 19 | 20 | #[Route('/claims/send', methods: ['POST'])] 21 | public function sendNew(ClaimDTO $claimDTO): Response 22 | { 23 | $claimDTO->setIsDraft(false); 24 | $claim = $this->claimService->create($claimDTO); 25 | return new JsonResponse(ClaimDTO::from($claim)); 26 | } 27 | 28 | #[Route('/claims/{id}/markInProcess', methods: ['POST'])] 29 | public function markAsInProcess(int $id): Response 30 | { 31 | $claim = $this->claimService->setStatus(Claim::STATUS_IN_PROCESS, $id); 32 | return new JsonResponse(ClaimDTO::from($claim)); 33 | } 34 | 35 | #[Route('/claims/{id}', methods: ['GET'])] 36 | public function find(int $id): Response 37 | { 38 | $claim = $this->claimService->find($id); 39 | return new JsonResponse(ClaimDTO::from($claim)); 40 | } 41 | 42 | #[Route('/claims/{id}', methods: ['POST'])] 43 | public function tryToAutomaticallyResolve(int $id): Response 44 | { 45 | $claim = $this->claimService->tryToResolveAutomatically($id); 46 | return new JsonResponse(ClaimDTO::from($claim)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Crm/Claims/ClaimNumberGenerator.php: -------------------------------------------------------------------------------- 1 | claimRepository = $claimRepository; 12 | } 13 | 14 | public function generate(Claim $claim): string 15 | { 16 | $count = $this->claimRepository->count(); 17 | $prefix = $count; 18 | if($count===0) { 19 | $prefix=1; 20 | } 21 | 22 | return sprintf('%s---%s', $prefix, $claim->getCreationDate()->format('d/m/Y')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Crm/Claims/ClaimRepository.php: -------------------------------------------------------------------------------- 1 | em->getRepository(Claim::class)->count([]); 14 | } 15 | 16 | public function getOne(int $claimId): ?Claim 17 | { 18 | return $this->em->find(Claim::class, $claimId); 19 | } 20 | 21 | public function save(Claim $claim): Claim 22 | { 23 | $this->em->persist($claim); 24 | $this->em->flush(); 25 | return $claim; 26 | } 27 | 28 | public function countByOwnerId(int $clientId): int 29 | { 30 | return $this->em->getRepository(Claim::class)->count(['ownerId' => $clientId]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Crm/Claims/ClaimResolver/Result.php: -------------------------------------------------------------------------------- 1 | whoToAsk = $whoToAsk; 26 | $this->decision = $decision; 27 | } 28 | 29 | public function getWhoToAsk(): string 30 | { 31 | return $this->whoToAsk; 32 | } 33 | 34 | public function getDecision(): string 35 | { 36 | return $this->decision; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Crm/Claims/ClaimsResolverRepository.php: -------------------------------------------------------------------------------- 1 | em->getRepository(ClaimsResolver::class)->findOneBy(['clientId' => $clientId]); 14 | } 15 | 16 | public function save(ClaimsResolver $claimsResolver): ClaimsResolver 17 | { 18 | $this->em->persist($claimsResolver); 19 | return $claimsResolver; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Crm/ClientController.php: -------------------------------------------------------------------------------- 1 | clientService->registerClient($dto->getName(), $dto->getLastName(), $dto->getType(), $dto->getDefaultPaymentType()); 17 | return new JsonResponse($this->clientService->load($c->getId())); 18 | } 19 | 20 | #[Route('/clients/{clientId}', methods: ['GET'])] 21 | public function find(int $clientId): Response 22 | { 23 | return new JsonResponse($this->clientService->load($clientId)); 24 | } 25 | 26 | #[Route('/clients/{clientId}/upgrade', methods: ['POST'])] 27 | public function upgradeToVIP(int $clientId): Response 28 | { 29 | $this->clientService->upgradeToVIP($clientId); 30 | return new JsonResponse($this->clientService->load($clientId)); 31 | } 32 | 33 | #[Route('/clients/{clientId}/downgrade', methods: ['POST'])] 34 | public function downgrade(int $clientId): Response 35 | { 36 | $this->clientService->downgradeToRegular($clientId); 37 | return new JsonResponse($this->clientService->load($clientId)); 38 | } 39 | 40 | #[Route('/clients/{clientId}/changeDefaultPaymentType', methods: ['POST'])] 41 | public function changeDefaultPaymentType(int $clientId, ClientDTO $clientDTO): Response 42 | { 43 | $this->clientService->changeDefaultPaymentType($clientId, $clientDTO->getDefaultPaymentType()); 44 | return new JsonResponse($this->clientService->load($clientId)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Crm/ClientDTO.php: -------------------------------------------------------------------------------- 1 | id = $client->getId(); 18 | $this->type = $client->getType(); 19 | $this->name = $client->getName(); 20 | $this->lastName = $client->getLastName(); 21 | $this->defaultPaymentType = $client->getDefaultPaymentType(); 22 | $this->clientType = $client->getClientType(); 23 | } 24 | 25 | public static function from(Client $client): self 26 | { 27 | return new self($client); 28 | } 29 | 30 | public function getId(): int 31 | { 32 | return $this->id; 33 | } 34 | 35 | public function getType(): string 36 | { 37 | return $this->type; 38 | } 39 | 40 | public function getName(): string 41 | { 42 | return $this->name; 43 | } 44 | 45 | public function getLastName(): string 46 | { 47 | return $this->lastName; 48 | } 49 | 50 | public function getDefaultPaymentType(): string 51 | { 52 | return $this->defaultPaymentType; 53 | } 54 | 55 | public function getClientType(): string 56 | { 57 | return $this->clientType; 58 | } 59 | 60 | public function jsonSerialize(): array 61 | { 62 | return [ 63 | 'id' => $this->id, 64 | 'type' => $this->type, 65 | 'name' => $this->name, 66 | 'lastName' => $this->lastName, 67 | 'paymentType' => $this->defaultPaymentType, 68 | 'clientType' => $this->clientType 69 | ]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Crm/ClientRepository.php: -------------------------------------------------------------------------------- 1 | em = $em; 14 | } 15 | 16 | public function getOne(int $clientId): ?Client 17 | { 18 | return $this->em->find(Client::class, $clientId); 19 | } 20 | 21 | public function save(Client $client): Client 22 | { 23 | $this->em->persist($client); 24 | $this->em->flush(); 25 | return $client; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Crm/TransitAnalyzer/AnalyzedAddressesDTO.php: -------------------------------------------------------------------------------- 1 | addresses = $addresses; 20 | } 21 | 22 | public function jsonSerialize(): array 23 | { 24 | return [ 25 | 'addresses' => $this->addresses 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Crm/TransitAnalyzer/PopulateGraphService.php: -------------------------------------------------------------------------------- 1 | transitDetailsFacade->findCompleted() as $transit) { 19 | $transitDetails = $this->transitDetailsFacade->find($transit->transitId); 20 | $this->graphTransitAnalyzer->addTransitBetweenAddresses( 21 | $transitDetails->client->getId(), 22 | $transit->transitId, 23 | $transitDetails->from->getHash(), 24 | $transitDetails->to->getHash(), 25 | $transitDetails->started, 26 | $transitDetails->completedAt 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Crm/TransitAnalyzer/TransitAnalyzerController.php: -------------------------------------------------------------------------------- 1 | analyzer->analyze($clientId, $this->addressRepository->findHashById($addressId)); 22 | return new JsonResponse(new AnalyzedAddressesDTO(array_map( 23 | fn (int $hash) => AddressDTO::from($this->addressRepository->getByHash($hash)), 24 | $addresses 25 | ))); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverAttributeDTO.php: -------------------------------------------------------------------------------- 1 | name = $name; 13 | $this->value = $value; 14 | } 15 | 16 | public static function with(string $name, string $value): self 17 | { 18 | return new self($name, $value); 19 | } 20 | 21 | public static function from(DriverAttribute $driverAttribute): self 22 | { 23 | return new self($driverAttribute->getName(), $driverAttribute->getValue()); 24 | } 25 | 26 | public function jsonSerialize(): array 27 | { 28 | return [ 29 | 'name' => $this->name, 30 | 'value' => $this->value 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverAttributeRepository.php: -------------------------------------------------------------------------------- 1 | em->persist($driverAttribute); 14 | $this->em->flush(); 15 | return $driverAttribute; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverController.php: -------------------------------------------------------------------------------- 1 | toArray(); 20 | $driver = $this->driverService->createDriver($data['license'], $data['lastName'], $data['firstName'], Driver::TYPE_CANDIDATE, Driver::STATUS_INACTIVE, $data['photo'] ?? null); 21 | return new JsonResponse($this->driverService->load($driver->getId())); 22 | } 23 | 24 | #[Route('/drivers/{id}', methods: ['GET'], )] 25 | public function getDriver(int $id): Response {return new JsonResponse($this->driverService->load($id));} 26 | 27 | #[Route('/drivers/{id}', methods: ['POST'], )] 28 | public function updateDriver(int $id): Response 29 | { 30 | 31 | return new JsonResponse($this->driverService->load($id)); 32 | } 33 | 34 | #[Route('/drivers/{id}/deactivate', methods: ['POST'], )] 35 | public function deactivateDriver(int $id): Response 36 | { 37 | $this->driverService->changeDriverStatus($id, Driver::STATUS_INACTIVE); 38 | return new JsonResponse($this->driverService->load($id)); 39 | } 40 | 41 | #[Route('/drivers/{id}/activate', methods: ['POST'], )] 42 | public function activateDriver(int $id): Response 43 | { 44 | $this->driverService->changeDriverStatus($id, Driver::STATUS_ACTIVE); 45 | return new JsonResponse($this->driverService->load($id)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverFee.php: -------------------------------------------------------------------------------- 1 | type = $type; 32 | $this->driver = $driver; 33 | $this->amount = $amount; 34 | $this->min = $min; 35 | } 36 | 37 | public function getType(): string 38 | { 39 | return $this->type; 40 | } 41 | 42 | public function setType(string $type): void 43 | { 44 | $this->type = $type; 45 | } 46 | 47 | public function getDriver(): Driver 48 | { 49 | return $this->driver; 50 | } 51 | 52 | public function setDriver(Driver $driver): void 53 | { 54 | $this->driver = $driver; 55 | } 56 | 57 | public function getAmount(): int 58 | { 59 | return $this->amount; 60 | } 61 | 62 | public function setAmount(int $amount): void 63 | { 64 | $this->amount = $amount; 65 | } 66 | 67 | public function getMin(): ?Money 68 | { 69 | return $this->min; 70 | } 71 | 72 | public function setMin(?Money $min): void 73 | { 74 | $this->min = $min; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverFeeRepository.php: -------------------------------------------------------------------------------- 1 | em->getRepository(DriverFee::class)->findOneBy(['driver' => $driverId]); 14 | } 15 | 16 | public function save(DriverFee $driverFee): DriverFee 17 | { 18 | $this->em->persist($driverFee); 19 | $this->em->flush(); 20 | return $driverFee; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverFeeService.php: -------------------------------------------------------------------------------- 1 | driverFeeRepository->findByDriverId($driverId); 17 | if($driverFee === null) { 18 | throw new \InvalidArgumentException('driver Fees not defined for driver, driver id = '.$driverId); 19 | } 20 | if($driverFee->getType() === DriverFee::TYPE_FLAT) { 21 | $finalFee = $transitPrice->subtract(Money::from($driverFee->getAmount())); 22 | } else { 23 | $finalFee = $transitPrice->percentage($driverFee->getAmount()); 24 | } 25 | 26 | return Money::from((int) max($finalFee->toInt(), $driverFee->getMin() === null ? 0 : $driverFee->getMin()->toInt())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverLicense.php: -------------------------------------------------------------------------------- 1 | driverLicense = $driverLicense; 19 | } 20 | 21 | public static function withLicense(string $driverLicense): self 22 | { 23 | if($driverLicense === '' || preg_match(self::DRIVER_LICENSE_REGEX, $driverLicense) !== 1) { 24 | throw new \InvalidArgumentException('Illegal license no = '.$driverLicense); 25 | } 26 | 27 | return new self($driverLicense); 28 | } 29 | 30 | public static function withoutValidation(string $driverLicense): self 31 | { 32 | return new self($driverLicense); 33 | } 34 | 35 | public function asString(): string 36 | { 37 | return $this->driverLicense; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverReport/DriverReport.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | private array $sessions = []; 23 | 24 | public function __construct() 25 | { 26 | } 27 | 28 | public function getDriverDTO(): DriverDTO 29 | { 30 | return $this->driverDTO; 31 | } 32 | 33 | public function setDriverDTO(DriverDTO $driverDTO): void 34 | { 35 | $this->driverDTO = $driverDTO; 36 | } 37 | 38 | public function getAttributes(): array 39 | { 40 | return $this->attributes; 41 | } 42 | 43 | /** 44 | * @param DriverAttributeDTO[] $attributes 45 | */ 46 | public function setAttributes(array $attributes): void 47 | { 48 | $this->attributes = $attributes; 49 | } 50 | 51 | public function addAttr(string $name, string $value): void 52 | { 53 | $this->attributes[] = DriverAttributeDTO::with($name, $value); 54 | } 55 | 56 | public function getSessions(): array 57 | { 58 | return $this->sessions; 59 | } 60 | 61 | /** 62 | * @param array $sessions 63 | */ 64 | public function setSessions(array $sessions): void 65 | { 66 | $this->sessions = $sessions; 67 | } 68 | 69 | public function jsonSerialize() 70 | { 71 | return [ 72 | 'driver' => $this->driverDTO, 73 | 'attributes' => $this->attributes, 74 | 'sessions' => $this->sessions 75 | ]; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverReport/DriverReportController.php: -------------------------------------------------------------------------------- 1 | driverReportCreator->createReport($driverId, (int) $request->get('lastDays', 1))); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/DriverFleet/DriverRepository.php: -------------------------------------------------------------------------------- 1 | em->persist($driver); 14 | $this->em->flush(); 15 | return $driver; 16 | } 17 | 18 | public function getOne(int $driverId): ?Driver 19 | { 20 | return $this->em->find(Driver::class, $driverId); 21 | } 22 | 23 | /** 24 | * @param int[] $ids 25 | * @return Driver[] 26 | */ 27 | public function findAllByIds(array $ids): array 28 | { 29 | return $this->em->getRepository(Driver::class)->findBy(['id' => $ids]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Geolocation/Address/AddressRepository.php: -------------------------------------------------------------------------------- 1 | em = $em; 14 | } 15 | 16 | public function getOne(int $addressId): ?Address 17 | { 18 | return $this->em->find(Address::class, $addressId); 19 | } 20 | 21 | // FIX ME: To replace with getOrCreate method instead of that? 22 | // Actual workaround for address uniqueness problem: assign result from repo.save to variable for later usage 23 | public function save(Address $address): Address 24 | { 25 | $address->hash(); 26 | if(!$this->isIdSet($address)) { 27 | $existingAddress = $this->em->getRepository(Address::class)->findOneBy(['hash' => $address->getHash()]); 28 | if($existingAddress !== null) { 29 | return $existingAddress; 30 | } 31 | } 32 | $this->em->persist($address); 33 | $this->em->flush(); 34 | return $address; 35 | } 36 | 37 | public function findHashById(int $id): int 38 | { 39 | return (int) $this->em->getConnection()->executeQuery( 40 | 'SELECT hash FROM address WHERE id = :id', 41 | ['id' => $id] 42 | )->fetchOne(); 43 | } 44 | 45 | public function getByHash(int $hash): Address 46 | { 47 | return $this->em->getRepository(Address::class)->findOneBy(['hash' => $hash]); 48 | } 49 | 50 | private function isIdSet(Address $address): bool 51 | { 52 | try { 53 | $address->getId(); 54 | return true; 55 | } catch (\Throwable $exception) { 56 | return false; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Geolocation/Distance.php: -------------------------------------------------------------------------------- 1 | km + $travelled->km); 26 | } 27 | 28 | public function toKmInFloat(): float 29 | { 30 | return $this->km; 31 | } 32 | 33 | public function printIn(string $unit): string 34 | { 35 | if($unit === 'km') { 36 | if($this->km === ceil($this->km)) { 37 | return sprintf('%d', round($this->km)).'km'; 38 | } 39 | return sprintf('%.3f', $this->km).'km'; 40 | } 41 | if($unit === 'miles') { 42 | $distance = $this->km / self::MILES_TO_KILOMETERS_RATIO; 43 | if($distance === ceil($distance)) { 44 | return sprintf('%d', round($distance)).'miles'; 45 | } 46 | return sprintf('%.3f', $distance).'miles'; 47 | } 48 | if($unit === 'm') { 49 | return sprintf('%d', round($this->km * 1000)).'m'; 50 | } 51 | throw new \InvalidArgumentException('Invalid unit '.$unit); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Geolocation/DistanceCalculator.php: -------------------------------------------------------------------------------- 1 | toKmInFloat(); 27 | } 28 | 29 | public function requiresSQLCommentHint(AbstractPlatform $platform) 30 | { 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Geolocation/GeocodingService.php: -------------------------------------------------------------------------------- 1 | amount = $amount; 21 | $this->subjectName = $subjectName; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Invocing/InvoiceGenerator.php: -------------------------------------------------------------------------------- 1 | invoiceRepository = $invoiceRepository; 12 | } 13 | 14 | public function generate(float $amount, string $subjectName): Invoice 15 | { 16 | return $this->invoiceRepository->save(new Invoice($amount, $subjectName)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Invocing/InvoiceRepository.php: -------------------------------------------------------------------------------- 1 | em->persist($invoice); 14 | $this->em->flush(); 15 | return $invoice; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Kernel.php: -------------------------------------------------------------------------------- 1 | import('../config/{packages}/*.yaml'); 19 | $container->import('../config/{packages}/'.$this->environment.'/*.yaml'); 20 | 21 | if (is_file(\dirname(__DIR__).'/config/services.yaml')) { 22 | $container->import('../config/services.yaml'); 23 | $container->import('../config/{services}_'.$this->environment.'.yaml'); 24 | } else { 25 | $container->import('../config/{services}.php'); 26 | } 27 | } 28 | 29 | protected function configureRoutes(RoutingConfigurator $routes): void 30 | { 31 | $routes->import('../config/{routes}/'.$this->environment.'/*.yaml'); 32 | $routes->import('../config/{routes}/*.yaml'); 33 | 34 | if (is_file(\dirname(__DIR__).'/config/routes.yaml')) { 35 | $routes->import('../config/routes.yaml'); 36 | } else { 37 | $routes->import('../config/{routes}.php'); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Loyalty/AwardsAccountDTO.php: -------------------------------------------------------------------------------- 1 | clientDTO = $clientDTO; 17 | $this->date = $account->getDate(); 18 | $this->isActive = $account->isActive(); 19 | $this->transactions = $account->getTransactions(); 20 | } 21 | 22 | public static function from(AwardsAccount $account, ClientDTO $clientDTO): self 23 | { 24 | return new self($account, $clientDTO); 25 | } 26 | 27 | public function getClient(): ClientDTO 28 | { 29 | return $this->clientDTO; 30 | } 31 | 32 | public function isActive(): bool 33 | { 34 | return $this->isActive; 35 | } 36 | 37 | public function getTransactions(): int 38 | { 39 | return $this->transactions; 40 | } 41 | 42 | public function jsonSerialize(): array 43 | { 44 | return [ 45 | 'client' => $this->clientDTO, 46 | 'date' => $this->date->format('Y-m-d H:i:s'), 47 | 'active' => $this->isActive, 48 | 'transactions' => $this->transactions 49 | ]; 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/Loyalty/AwardsAccountRepository.php: -------------------------------------------------------------------------------- 1 | em->getRepository(AwardsAccount::class)->findOneBy(['clientId' => $clientId]); 15 | } 16 | 17 | public function save(AwardsAccount $account): void 18 | { 19 | $this->em->persist($account); 20 | $this->em->flush(); 21 | } 22 | 23 | /** 24 | * @return AwardedMiles[] 25 | */ 26 | public function findAllMilesBy(Client $client): array 27 | { 28 | return $this->findByClientId($client->getId())?->getMiles() ?? []; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Loyalty/AwardsService.php: -------------------------------------------------------------------------------- 1 | amount = $amount; 13 | $this->whenExpires = $whenExpires; 14 | } 15 | 16 | public static function untilForever(int $amount): self 17 | { 18 | return new self($amount, null); 19 | } 20 | 21 | public static function until(int $amount, \DateTimeImmutable $when): self 22 | { 23 | return new self($amount, $when); 24 | } 25 | 26 | public function getAmountFor(\DateTimeImmutable $moment): int 27 | { 28 | return $this->whenExpires === null || $this->whenExpires >= $moment ? $this->amount : 0; 29 | } 30 | 31 | public function subtract(int $amount, \DateTimeImmutable $moment): Miles 32 | { 33 | if($this->getAmountFor($moment) < $amount) { 34 | throw new \InvalidArgumentException('Insufficient amount of miles'); 35 | } 36 | return new self($this->amount - $amount, $this->whenExpires); 37 | } 38 | 39 | public function expiresAt(): ?\DateTimeImmutable 40 | { 41 | return $this->whenExpires; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Loyalty/Miles.php: -------------------------------------------------------------------------------- 1 | amount = $amount; 14 | $this->whenFirstHalfExpires = $whenFirstHalfExpires; 15 | $this->whenExpires = $whenExpires; 16 | } 17 | 18 | public function getAmountFor(\DateTimeImmutable $moment): int 19 | { 20 | if($this->whenFirstHalfExpires >= $moment) { 21 | return $this->amount; 22 | } 23 | 24 | if($this->whenExpires >= $moment) { 25 | return $this->amount - $this->halfOf($this->amount); 26 | } 27 | 28 | return 0; 29 | } 30 | 31 | private function halfOf(int $amount): int 32 | { 33 | return $amount / 2; 34 | } 35 | 36 | public function subtract(int $amount, \DateTimeImmutable $moment): Miles 37 | { 38 | $currentAmount = $this->getAmountFor($moment); 39 | if($currentAmount < $amount) { 40 | throw new \InvalidArgumentException('Insufficient amount of miles'); 41 | } 42 | return new self($currentAmount - $amount, $this->whenFirstHalfExpires, $this->whenExpires); 43 | } 44 | 45 | public function expiresAt(): ?\DateTimeImmutable 46 | { 47 | return $this->whenExpires; 48 | } 49 | 50 | public function getWhenFirstHalfExpires(): \DateTimeImmutable 51 | { 52 | return $this->whenFirstHalfExpires; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Money/Money.php: -------------------------------------------------------------------------------- 1 | value = $value; 12 | } 13 | 14 | public static function from(int $value): self 15 | { 16 | return new self($value); 17 | } 18 | 19 | public static function zero(): self 20 | { 21 | return new self(0); 22 | } 23 | 24 | public function add(self $other): self 25 | { 26 | return new self($this->value + $other->value); 27 | } 28 | 29 | public function subtract(self $other): self 30 | { 31 | return new self($this->value - $other->value); 32 | } 33 | 34 | public function percentage(int $percentage): self 35 | { 36 | return new self((int) round($percentage * $this->value/100)); 37 | } 38 | 39 | public function toInt(): int 40 | { 41 | return $this->value; 42 | } 43 | 44 | public function toString(): string 45 | { 46 | return sprintf('%.2f', $this->value / 100); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Money/MoneyType.php: -------------------------------------------------------------------------------- 1 | toInt(); 27 | } 28 | 29 | public function requiresSQLCommentHint(AbstractPlatform $platform) 30 | { 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Notification/ClientNotificationService.php: -------------------------------------------------------------------------------- 1 | id = Uuid::v4(); 14 | } 15 | 16 | public function toUuid(): Uuid 17 | { 18 | return $this->id; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Party/Api/PartyMapper.php: -------------------------------------------------------------------------------- 1 | repository = $repository; 16 | } 17 | 18 | /** 19 | * @return Option 20 | */ 21 | public function mapRelation(PartyId $id, string $relationshipName): Option 22 | { 23 | return $this->repository->findRelationshipFor($id, $relationshipName); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Party/Api/RoleObjectFactory.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | private array $roles = []; 20 | 21 | public static function from(PartyRelationship $relationship): self 22 | { 23 | $roleObject = new self(); 24 | $roleObject->addRelationship($relationship); 25 | 26 | return $roleObject; 27 | } 28 | 29 | public function hasRole(string $role): bool 30 | { 31 | return isset($this->roles[$role]); 32 | } 33 | 34 | public function getRole(string $class): Option 35 | { 36 | return Option::of($this->roles[$class] ?? null); 37 | } 38 | 39 | private function addRelationship(PartyRelationship $relationship): void 40 | { 41 | $this->add($relationship->getRoleA(), $relationship->getPartyA()); 42 | $this->add($relationship->getRoleB(), $relationship->getPartyB()); 43 | } 44 | 45 | private function add(string $role, Party $party): void 46 | { 47 | //in sake of simplicity: a role name is same as a class name with no mapping between them 48 | if(!class_exists($role) || !is_subclass_of($role, PartyBasedRole::class)) { 49 | throw new \InvalidArgumentException(); 50 | } 51 | $parentClass = get_parent_class($role); 52 | $key = $parentClass !== PartyBasedRole::class ? $parentClass : $role; 53 | 54 | $this->roles[$key] = new $role($party); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Party/Infra/DoctrinePartyRepository.php: -------------------------------------------------------------------------------- 1 | em->find(Party::class, $id); 19 | if($party === null) { 20 | $party = new Party($id); 21 | $this->em->persist($party); 22 | $this->em->flush(); 23 | } 24 | 25 | return $party; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/Party/Model/Party/Party.php: -------------------------------------------------------------------------------- 1 | id = $id; 20 | } 21 | 22 | public function getId(): Uuid 23 | { 24 | return $this->id; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Party/Model/Party/PartyRelationship.php: -------------------------------------------------------------------------------- 1 | id = Uuid::v4(); 36 | } 37 | 38 | public function getName(): string 39 | { 40 | return $this->name; 41 | } 42 | 43 | public function setName(string $name): void 44 | { 45 | $this->name = $name; 46 | } 47 | 48 | public function getRoleA(): string 49 | { 50 | return $this->roleA; 51 | } 52 | 53 | public function setRoleA(string $roleA): void 54 | { 55 | $this->roleA = $roleA; 56 | } 57 | 58 | public function getRoleB(): string 59 | { 60 | return $this->roleB; 61 | } 62 | 63 | public function setRoleB(string $roleB): void 64 | { 65 | $this->roleB = $roleB; 66 | } 67 | 68 | public function getPartyA(): Party 69 | { 70 | return $this->partyA; 71 | } 72 | 73 | public function setPartyA(Party $partyA): void 74 | { 75 | $this->partyA = $partyA; 76 | } 77 | 78 | public function getPartyB(): Party 79 | { 80 | return $this->partyB; 81 | } 82 | 83 | public function setPartyB(Party $partyB): void 84 | { 85 | $this->partyB = $partyB; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Party/Model/Party/PartyRelationshipRepository.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | public function findRelationshipFor(PartyId $id, string $relationshipName): Option; 16 | } 17 | -------------------------------------------------------------------------------- /src/Party/Model/Party/PartyRepository.php: -------------------------------------------------------------------------------- 1 | id = Uuid::v4(); 22 | } 23 | 24 | public function getName(): string 25 | { 26 | return $this->name; 27 | } 28 | 29 | public function setName(string $name): void 30 | { 31 | $this->name = $name; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Party/Model/Role/PartyBasedRole.php: -------------------------------------------------------------------------------- 1 | party = $party; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Pricing/Tariffs.php: -------------------------------------------------------------------------------- 1 | partyRepository->put($insurerId->toUuid()); 21 | $vehicle = $this->partyRepository->put($vehicleId->toUuid()); 22 | 23 | $this->partyRelationshipRepository->put( 24 | PartyRelationshipsDictionary::REPAIR, 25 | PartyRolesDictionary::INSURER, $insurer, 26 | PartyRolesDictionary::INSURED, $vehicle 27 | ); 28 | } 29 | 30 | public function manufacturerWarrantyRegistered(PartyId $distributorId, PartyId $vehicleId): void 31 | { 32 | $distributor = $this->partyRepository->put($distributorId->toUuid()); 33 | $vehicle = $this->partyRepository->put($vehicleId->toUuid()); 34 | 35 | $this->partyRelationshipRepository->put( 36 | PartyRelationshipsDictionary::REPAIR, 37 | PartyRolesDictionary::GUARANTOR, $distributor, 38 | PartyRolesDictionary::CUSTOMER, $vehicle 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Repair/Api/RepairRequest.php: -------------------------------------------------------------------------------- 1 | vehicle = $vehicle; 19 | $this->partsToRepair = $partsToRepair; 20 | } 21 | 22 | public function getVehicle(): PartyId 23 | { 24 | return $this->vehicle; 25 | } 26 | 27 | public function getPartsToRepair(): array 28 | { 29 | return $this->partsToRepair; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Repair/Api/ResolveResult.php: -------------------------------------------------------------------------------- 1 | handlingParty = $handlingParty; 21 | $this->totalCost = $totalCost; 22 | $this->acceptedParts = $acceptedParts; 23 | $this->status = $status; 24 | } 25 | 26 | public function getHandlingParty(): ?Uuid 27 | { 28 | return $this->handlingParty; 29 | } 30 | 31 | public function getTotalCost(): ?Money 32 | { 33 | return $this->totalCost; 34 | } 35 | 36 | public function getAcceptedParts(): array 37 | { 38 | return $this->acceptedParts; 39 | } 40 | 41 | public function getStatus(): string 42 | { 43 | return $this->status; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Repair/Legacy/DAO/UserDAO.php: -------------------------------------------------------------------------------- 1 | setCoveredParts([Parts::ENGINE, Parts::GEARBOX, Parts::PAINT, Parts::SUSPENSION]); 19 | $contract->setCoverageRatio(100); 20 | 21 | $user = new EmployeeDriverWithOwnCar(); 22 | $user->setContract($contract); 23 | 24 | return $user; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Repair/Legacy/Job/CommonBaseAbstractJob.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | private array $params = []; 17 | 18 | public function __construct(string $decision) 19 | { 20 | if(!in_array($decision, [self::DECISION_ACCEPTED, self::DECISION_ERROR, self::DECISION_REDIRECTION])) { 21 | throw new \InvalidArgumentException(); 22 | } 23 | 24 | $this->decision = $decision; 25 | } 26 | 27 | public function addParam(string $name, $value): self 28 | { 29 | $this->params[$name] = $value; 30 | 31 | return $this; 32 | } 33 | 34 | /** 35 | * @return mixed|null 36 | */ 37 | public function getParam(string $name) 38 | { 39 | return $this->params[$name] ?? null; 40 | } 41 | 42 | public function getDecision(): string 43 | { 44 | return $this->decision; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Repair/Legacy/Job/MaintenanceJob.php: -------------------------------------------------------------------------------- 1 | partsToRepair = []; 18 | $this->estimatedValue = Money::zero(); 19 | } 20 | 21 | public function getPartsToRepair(): array 22 | { 23 | return $this->partsToRepair; 24 | } 25 | 26 | public function getEstimatedValue(): Money 27 | { 28 | return $this->estimatedValue; 29 | } 30 | 31 | /** 32 | * @param string[] $partsToRepair 33 | */ 34 | public function setPartsToRepair(array $partsToRepair): void 35 | { 36 | $this->partsToRepair = $partsToRepair; 37 | } 38 | 39 | public function setEstimatedValue(Money $estimatedValue): void 40 | { 41 | $this->estimatedValue = $estimatedValue; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Repair/Legacy/Parts/Parts.php: -------------------------------------------------------------------------------- 1 | userDAO = $userDAO; 16 | } 17 | 18 | public function repair(int $userId, CommonBaseAbstractJob $job): JobResult 19 | { 20 | $user = $this->userDAO->getOne($userId); 21 | return $user->doJob($job); 22 | } 23 | 24 | public function repair2parallelModels(int $userId, CommonBaseAbstractJob $job): JobResult 25 | { 26 | $user = $this->userDAO->getOne($userId); 27 | return $user->doJob($job); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Repair/Legacy/User/CommonBaseAbstractUser.php: -------------------------------------------------------------------------------- 1 | handleRepairJob($job); 18 | } 19 | if($job instanceof MaintenanceJob) { 20 | return $this->handleMaintenanceJob($job); 21 | } 22 | 23 | return $this->defaultHandler($job); 24 | } 25 | 26 | protected function handleRepairJob(RepairJob $job): JobResult 27 | { 28 | return $this->defaultHandler($job); 29 | } 30 | 31 | protected function handleMaintenanceJob(MaintenanceJob $job): JobResult 32 | { 33 | return $this->defaultHandler($job); 34 | } 35 | 36 | protected function defaultHandler(CommonBaseAbstractJob $job): JobResult 37 | { 38 | throw new \InvalidArgumentException(sprintf('%s can not handle %s', static::class, get_class($job))); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Repair/Legacy/User/EmployeeDriver.php: -------------------------------------------------------------------------------- 1 | addParam('shouldHandleBy', $this->lasingCompanyId); 15 | } 16 | 17 | public function getLasingCompanyId(): int 18 | { 19 | return $this->lasingCompanyId; 20 | } 21 | 22 | public function setLasingCompanyId(int $lasingCompanyId): void 23 | { 24 | $this->lasingCompanyId = $lasingCompanyId; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Repair/Legacy/User/EmployeeDriverWithOwnCar.php: -------------------------------------------------------------------------------- 1 | getPartsToRepair(), $this->contract->getCoveredParts()); 15 | 16 | $coveredCost = $job->getEstimatedValue()->percentage((int) $this->contract->getCoverageRatio()); 17 | $totalCost = $job->getEstimatedValue()->subtract($coveredCost); 18 | 19 | return (new JobResult(JobResult::DECISION_ACCEPTED))->addParam('totalCost', $totalCost)->addParam('acceptedParts',$acceptedParts); 20 | } 21 | 22 | 23 | public function setContract(SignedContract $contract): void 24 | { 25 | $this->contract = $contract; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Repair/Legacy/User/SignedContract.php: -------------------------------------------------------------------------------- 1 | coveredParts; 27 | } 28 | 29 | /** 30 | * @param string[] $coveredParts 31 | */ 32 | public function setCoveredParts(array $coveredParts): void 33 | { 34 | $this->coveredParts = $coveredParts; 35 | } 36 | 37 | public function getCoverageRatio(): float 38 | { 39 | return $this->coverageRatio; 40 | } 41 | 42 | public function setCoverageRatio(float $coverageRatio): void 43 | { 44 | $this->coverageRatio = $coverageRatio; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Repair/Legacy/User/SubcontractorDriver.php: -------------------------------------------------------------------------------- 1 | getPartsToRepair(), fn(string $part) => $part !== Parts::PAINT); 14 | 15 | return new RepairingResult($this->party->getId(), Money::zero(), $handledParts); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/Repair/Model/Roles/Repair/RepairingResult.php: -------------------------------------------------------------------------------- 1 | handlingParty = $handlingParty; 17 | $this->totalCost = $totalCost; 18 | $this->handledParts = $handledParts; 19 | } 20 | 21 | public function getHandlingParty(): Uuid 22 | { 23 | return $this->handlingParty; 24 | } 25 | 26 | public function getTotalCost(): Money 27 | { 28 | return $this->totalCost; 29 | } 30 | 31 | public function getHandledParts(): array 32 | { 33 | return $this->handledParts; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Repair/Model/Roles/Repair/RoleForRepairer.php: -------------------------------------------------------------------------------- 1 | party->getId(), Money::zero(), $request->getPartsToRepair()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Ride/ChangeDestinationService.php: -------------------------------------------------------------------------------- 1 | geocodingService->geocodeAddress($from); 25 | $geoTo = $this->geocodingService->geocodeAddress($newAddress); 26 | $newDistance = Distance::ofKm($this->distanceCalculator->calculateByMap($geoFrom[0], $geoFrom[1], $geoTo[0], $geoTo[1])); 27 | $transit = $this->transitRepository->findByTransitRequestUuid($requestUuid); 28 | $transit?->changeDestination($newDistance); 29 | return $newDistance; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Ride/CompleteTransitService.php: -------------------------------------------------------------------------------- 1 | transitRepository->findByTransitRequestUuid($requestUuid); 25 | if($transit === null) { 26 | throw new \InvalidArgumentException('Transit does not exist, id = '.$requestUuid); 27 | } 28 | 29 | // FIXME later: add some exceptions handling 30 | $geoFrom = $this->geocodingService->geocodeAddress($from); 31 | $geoTo = $this->geocodingService->geocodeAddress($destinationAddress); 32 | $distance = Distance::ofKm($this->distanceCalculator->calculateByMap($geoFrom[0], $geoFrom[1], $geoTo[0], $geoTo[1])); 33 | $finalPrice = $transit->completeAt($distance); 34 | $this->transitRepository->save($transit); 35 | 36 | return $finalPrice; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Ride/DemandService.php: -------------------------------------------------------------------------------- 1 | transitDemandRepository->save(new TransitDemand($requestUuid)); 18 | } 19 | 20 | public function cancelDemand(Uuid $requestUuid): void 21 | { 22 | $transitDemand = $this->transitDemandRepository->findByRequestUuid($requestUuid); 23 | if($transitDemand !== null) { 24 | $transitDemand->cancel(); 25 | } 26 | } 27 | 28 | public function acceptDemand(Uuid $requestUuid): void 29 | { 30 | $transitDemand = $this->transitDemandRepository->findByRequestUuid($requestUuid); 31 | if($transitDemand !== null) { 32 | $transitDemand->accept(); 33 | } 34 | } 35 | 36 | public function existsFor(Uuid $requestUuid): bool 37 | { 38 | return $this->transitDemandRepository->findByRequestUuid($requestUuid) !== null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Ride/Details/Status.php: -------------------------------------------------------------------------------- 1 | clientId; 19 | } 20 | 21 | public function transitId(): int 22 | { 23 | return $this->transitId; 24 | } 25 | 26 | public function addressFromHash(): int 27 | { 28 | return $this->addressFromHash; 29 | } 30 | 31 | public function addressToHash(): int 32 | { 33 | return $this->addressToHash; 34 | } 35 | 36 | public function started(): \DateTimeImmutable 37 | { 38 | return $this->started; 39 | } 40 | 41 | public function completeAt(): \DateTimeImmutable 42 | { 43 | return $this->completeAt; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Ride/RequestForTransit.php: -------------------------------------------------------------------------------- 1 | requestUuid = Uuid::v4(); 29 | $this->tariff = $tariff; 30 | $this->distance = $distance; 31 | } 32 | 33 | public function getEstimatedPrice(): Money 34 | { 35 | return $this->tariff->calculateCost($this->distance); 36 | } 37 | 38 | public function getRequestUuid(): Uuid 39 | { 40 | return $this->requestUuid; 41 | } 42 | 43 | public function getTariff(): Tariff 44 | { 45 | return $this->tariff; 46 | } 47 | 48 | public function getDistance(): Distance 49 | { 50 | return $this->distance; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Ride/RequestForTransitRepository.php: -------------------------------------------------------------------------------- 1 | em->find(RequestForTransit::class, $id); 15 | } 16 | 17 | public function findByRequestUuid(Uuid $requestUuid): ?RequestForTransit 18 | { 19 | return $this->em->getRepository(RequestForTransit::class)->findOneBy([ 20 | 'requestUuid' => $requestUuid 21 | ]); 22 | } 23 | 24 | public function save(RequestForTransit $requestForTransit): RequestForTransit 25 | { 26 | $this->em->persist($requestForTransit); 27 | $this->em->flush(); 28 | return $requestForTransit; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Ride/RequestTransitService.php: -------------------------------------------------------------------------------- 1 | geocodingService->geocodeAddress($from); 30 | $geoTo = $this->geocodingService->geocodeAddress($to); 31 | $distance = Distance::ofKm($this->distanceCalculator->calculateByMap($geoFrom[0], $geoFrom[1], $geoTo[0], $geoTo[1])); 32 | $now = $this->clock->now(); 33 | $tariff = $this->tariffs->choose($now); 34 | return $this->requestForTransitRepository->save(new RequestForTransit($tariff, $distance)); 35 | } 36 | 37 | public function findCalculationUUID(int $requestId): Uuid 38 | { 39 | return $this->requestForTransitRepository->getOne($requestId)->getRequestUuid(); 40 | } 41 | 42 | public function findTariff(Uuid $requestUuid): Tariff 43 | { 44 | return $this->requestForTransitRepository->findByRequestUuid($requestUuid)->getTariff(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Ride/StartTransitService.php: -------------------------------------------------------------------------------- 1 | transitRepository->save(new Transit($this->requestTransitService->findTariff($requestUuid), $requestUuid)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Ride/TransitDemandRepository.php: -------------------------------------------------------------------------------- 1 | em->getRepository(TransitDemand::class)->findOneBy([ 15 | 'requestUuid' => $requestUuid 16 | ]); 17 | } 18 | 19 | public function save(TransitDemand $transitDemand): TransitDemand 20 | { 21 | $this->em->persist($transitDemand); 22 | $this->em->flush(); 23 | return $transitDemand; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Tracking/DriverPosition.php: -------------------------------------------------------------------------------- 1 | driverId = $driverId; 27 | $this->latitude = $latitude; 28 | $this->longitude = $longitude; 29 | $this->seenAt = $seenAt; 30 | } 31 | 32 | public function getDriverId(): int 33 | { 34 | return $this->driverId; 35 | } 36 | 37 | public function getLatitude(): float 38 | { 39 | return $this->latitude; 40 | } 41 | 42 | public function getLongitude(): float 43 | { 44 | return $this->longitude; 45 | } 46 | 47 | public function getSeenAt(): \DateTimeImmutable 48 | { 49 | return $this->seenAt; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Tracking/DriverPositionDTO.php: -------------------------------------------------------------------------------- 1 | driverId = $driverId; 15 | $this->latitude = $latitude; 16 | $this->longitude = $longitude; 17 | $this->seenAt = $seenAt; 18 | } 19 | 20 | public static function from(int $driverId, float $latitude, float $longitude, \DateTimeImmutable $seenAt): self 21 | { 22 | return new self($driverId, $latitude, $longitude, $seenAt); 23 | } 24 | 25 | public function getDriverId(): int 26 | { 27 | return $this->driverId; 28 | } 29 | 30 | public function getLatitude(): float 31 | { 32 | return $this->latitude; 33 | } 34 | 35 | public function getLongitude(): float 36 | { 37 | return $this->longitude; 38 | } 39 | 40 | public function getSeenAt(): \DateTimeImmutable 41 | { 42 | return $this->seenAt; 43 | } 44 | 45 | public function jsonSerialize(): array 46 | { 47 | return [ 48 | 'driverId' => $this->driverId, 49 | 'latitude' => $this->latitude, 50 | 'longitude' => $this->longitude, 51 | 'seenAt' => $this->seenAt->format('Y-m-d H:i:s') 52 | ]; 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/Tracking/DriverPositionDTOV2.php: -------------------------------------------------------------------------------- 1 | driverId = $driverId; 15 | $this->latitude = $latitude; 16 | $this->longitude = $longitude; 17 | $this->seenAt = $seenAt; 18 | } 19 | 20 | public function getDriverId(): int 21 | { 22 | return $this->driverId; 23 | } 24 | 25 | public function getLatitude(): float 26 | { 27 | return $this->latitude; 28 | } 29 | 30 | public function getLongitude(): float 31 | { 32 | return $this->longitude; 33 | } 34 | 35 | public function getSeenAt(): \DateTimeImmutable 36 | { 37 | return $this->seenAt; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Tracking/DriverPositionRepository.php: -------------------------------------------------------------------------------- 1 | em->persist($driverPosition); 15 | $this->em->flush(); 16 | return $driverPosition; 17 | } 18 | 19 | /** 20 | * @return DriverPositionDTOV2[] 21 | */ 22 | public function findAverageDriverPositionSince(float $latitudeMin, float $latitudeMax, float $longitudeMin, float $longitudeMax, \DateTimeImmutable $date): array 23 | { 24 | return \array_map( 25 | static fn (array $data): DriverPositionDTOV2 => new DriverPositionDTOV2($data['driver'], $data['lat'], $data['lon'], new \DateTimeImmutable($data['seen'])), 26 | $this->em->createQueryBuilder() 27 | ->select(\sprintf('d.id as driver, avg(p.latitude) as lat, avg(p.longitude) as lon, max(p.seenAt) as seen')) 28 | ->from(Driver::class, 'd') 29 | ->join(DriverPosition::class, 'p', 'WITH', 'p.driverId = d.id') 30 | ->where('p.latitude between :latitudeMin and :latitudeMax') 31 | ->andWhere('p.longitude between :longitudeMin and :longitudeMax') 32 | ->andWhere('p.seenAt >= :seenAt') 33 | ->setParameters([ 34 | 'latitudeMin' => $latitudeMin, 35 | 'latitudeMax' => $latitudeMax, 36 | 'longitudeMin' => $longitudeMin, 37 | 'longitudeMax' => $longitudeMax, 38 | 'seenAt' => $date 39 | ]) 40 | ->groupBy('d.id') 41 | ->getQuery() 42 | ->getResult()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Tracking/DriverSessionController.php: -------------------------------------------------------------------------------- 1 | driverSessionService->logIn($driverId, $driverSessionDTO->getPlatesNumber(), $driverSessionDTO->getCarClass(), $driverSessionDTO->getCarBrand()); 19 | return new JsonResponse(); 20 | } 21 | 22 | #[Route('/drivers/{driverId}/driverSessions/{sessionId}', methods: ['DELETE'])] 23 | public function logOut(int $driverId, int $sessionId): Response 24 | { 25 | $this->driverSessionService->logOut($sessionId); 26 | return new JsonResponse(); 27 | } 28 | 29 | #[Route('/drivers/{driverId}/driverSessions', methods: ['DELETE'])] 30 | public function logOutCurrent(int $driverId): Response 31 | { 32 | $this->driverSessionService->logOutCurrentSession($driverId); 33 | return new JsonResponse(); 34 | } 35 | 36 | #[Route('/drivers/{driverId}/driverSessions', methods: ['GET'])] 37 | public function list(int $driverId): Response 38 | { 39 | return new JsonResponse(array_map( 40 | fn(DriverSession $s) => DriverSessionDTO::from($s), 41 | $this->driverSessionService->findByDriver($driverId) 42 | )); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Tracking/DriverTrackingController.php: -------------------------------------------------------------------------------- 1 | trackingService->registerPosition($driverPositionDTO->getDriverId(), $driverPositionDTO->getLatitude(), $driverPositionDTO->getLongitude(), $driverPositionDTO->getSeenAt()); 18 | return new JsonResponse($this->toDto($driverPosition)); 19 | } 20 | 21 | #[Route('/driverPositions/{id}/total', methods: ['GET'])] 22 | public function calculateTravelledDistance(int $id, Request $request): Response 23 | { 24 | return new JsonResponse($this->trackingService->calculateTravelledDistance($id, new \DateTimeImmutable($request->get('from')), new \DateTimeImmutable($request->get('to')))->toKmInFloat()); 25 | } 26 | 27 | private function toDto(DriverPosition $driverPosition): DriverPositionDTO 28 | { 29 | return DriverPositionDTO::from( 30 | $driverPosition->getDriverId(), 31 | $driverPosition->getLatitude(), 32 | $driverPosition->getLongitude(), 33 | $driverPosition->getSeenAt() 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Common/AddressFixture.php: -------------------------------------------------------------------------------- 1 | setPostalCode('11-111'); 20 | $address->setName('Home'); 21 | $address->setDistrict('district'); 22 | return $this->addressRepository->save($address); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Common/AwardsAccountFixture.php: -------------------------------------------------------------------------------- 1 | awardsService->registerToProgram($client->getId()); 19 | } 20 | 21 | public function activeAwardsAccount(Client $client): void 22 | { 23 | $this->awardsAccount($client); 24 | $this->awardsService->activateAccount($client->getId()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Common/CarTypeFixture.php: -------------------------------------------------------------------------------- 1 | carTypeService->create($carTypeDTO); 23 | $this->carTypeService->registerCar($carClass); 24 | $this->carTypeService->activate($carType->getId()); 25 | return $carType; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Common/ClaimFixture.php: -------------------------------------------------------------------------------- 1 | getId(), $transit->getId()); 23 | $claimDto->setIsDraft(false); 24 | return $this->claimService->create($claimDto); 25 | } 26 | 27 | public function createAndResolveClaim(Client $client, TransitDTO $transit): Claim 28 | { 29 | $claim = $this->createClaim($client, $transit); 30 | $this->claimService->tryToResolveAutomatically($claim->getId()); 31 | return $claim; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Common/ClientFixture.php: -------------------------------------------------------------------------------- 1 | setName('Janusz'); 20 | $client->setLastName('Kowalski'); 21 | $client->setType($type); 22 | $client->setDefaultPaymentType(Client::PAYMENT_TYPE_MONTHLY_INVOICE); 23 | return $this->clientRepository->save($client); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Common/Factory.php: -------------------------------------------------------------------------------- 1 | fixed ?? new \DateTimeImmutable(); 14 | } 15 | 16 | public function setDateTime(?\DateTimeImmutable $dateTime): void 17 | { 18 | $this->fixed = $dateTime; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Common/PrivateProperty.php: -------------------------------------------------------------------------------- 1 | getProperty('id'); 11 | $property->setAccessible(true); 12 | $property->setValue($object, $value); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Common/StubbedTransitPrice.php: -------------------------------------------------------------------------------- 1 | fakeTariffs->setFakeTariff(Tariff::of(0, 'faked', $faked)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Common/TransitFixture.php: -------------------------------------------------------------------------------- 1 | transitRepository->save(new Transit(Tariff::of(0, 'fake', Money::from($price)), Uuid::v4())); 34 | $this->stubbedTransitPrice->stub(Money::from($price)); 35 | $this->transitDetailsFacade->transitRequested($when, $transit->getRequestUuid(), $from, $to, Distance::zero(), $client, CarType::CAR_CLASS_VAN, Money::from($price), Tariff::ofTime($when)); 36 | $this->transitDetailsFacade->transitAccepted($transit->getRequestUuid(), $when, $driver->getId()); 37 | $this->transitDetailsFacade->transitStarted($transit->getRequestUuid(), $transit->getId(), $when); 38 | $this->transitDetailsFacade->transitCompleted($transit->getRequestUuid(), $when, Money::from($price), Money::zero()); 39 | return $transit; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Double/FakeAppProperties.php: -------------------------------------------------------------------------------- 1 | automaticRefundForVipThreshold; 18 | } 19 | 20 | public function setAutomaticRefundForVipThreshold(int $automaticRefundForVipThreshold): void 21 | { 22 | $this->automaticRefundForVipThreshold = $automaticRefundForVipThreshold; 23 | } 24 | 25 | public function getNoOfTransitsForClaimAutomaticRefund(): int 26 | { 27 | return $this->noOfTransitsForClaimAutomaticRefund; 28 | } 29 | 30 | public function setNoOfTransitsForClaimAutomaticRefund(int $noOfTransitsForClaimAutomaticRefund): void 31 | { 32 | $this->noOfTransitsForClaimAutomaticRefund = $noOfTransitsForClaimAutomaticRefund; 33 | } 34 | 35 | public function getMilesExpirationInDays(): int 36 | { 37 | return $this->milesExpirationInDays; 38 | } 39 | 40 | public function setMilesExpirationInDays(int $milesExpirationInDays): void 41 | { 42 | $this->milesExpirationInDays = $milesExpirationInDays; 43 | } 44 | 45 | public function getDefaultMilesBonus(): int 46 | { 47 | return $this->defaultMilesBonus; 48 | } 49 | 50 | public function setDefaultMilesBonus(int $defaultMilesBonus): void 51 | { 52 | $this->defaultMilesBonus = $defaultMilesBonus; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Double/FakeGeocodingService.php: -------------------------------------------------------------------------------- 1 | addressMap[$address->getHash()])) { 16 | return $this->addressMap[$address->getHash()]; 17 | } 18 | 19 | if($this->returnValues !== []) { 20 | return array_shift($this->returnValues); 21 | } 22 | 23 | return [1.0, 1.0]; 24 | } 25 | 26 | public function setReturnValues(array $returnValues): void 27 | { 28 | $this->returnValues = $returnValues; 29 | } 30 | 31 | public function setValuesForAddress(Address $address, array $values): void 32 | { 33 | $this->addressMap[$address->getHash()] = $values; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Double/FakeTariffs.php: -------------------------------------------------------------------------------- 1 | fakeTariff = $tariff; 15 | } 16 | 17 | public function choose(?\DateTimeImmutable $when = null): Tariff 18 | { 19 | if($this->fakeTariff !== null) { 20 | return $this->fakeTariff; 21 | } 22 | 23 | return parent::choose($when); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /tests/Integration/Contracts/Application/Acme/Dynamic/DocumentOperationResultAssert.php: -------------------------------------------------------------------------------- 1 | result = $result; 16 | self::assertEquals(DocumentOperationResult::SUCCESS, $result->getResult()); 17 | } 18 | 19 | public function editable(): self 20 | { 21 | self::assertTrue($this->result->isContentChangePossible()); 22 | return $this; 23 | } 24 | 25 | public function uneditable(): self 26 | { 27 | self::assertFalse($this->result->isContentChangePossible()); 28 | return $this; 29 | } 30 | 31 | public function state(string $state): self 32 | { 33 | self::assertEquals($state, $this->result->getStateName()); 34 | return $this; 35 | } 36 | 37 | public function content(ContentId $contentId): self 38 | { 39 | self::assertEquals($contentId, $this->result->getContentId()); 40 | return $this; 41 | } 42 | 43 | public function possibleNextStates(string ...$states): self 44 | { 45 | self::assertEquals($states, array_keys($this->result->getPossibleTransitionsAndRules())); 46 | return $this; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Integration/Contracts/Application/Acme/Straightforward/ContractResultAssert.php: -------------------------------------------------------------------------------- 1 | result = $result; 16 | self::assertEquals(ContractResult::SUCCESS, $result->getResult()); 17 | } 18 | 19 | public function state(BaseState $state): self 20 | { 21 | self::assertEquals($state->getStateDescriptor(), $this->result->getStateDescriptor()); 22 | return $this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Integration/GraphTransitAnalyzerIntegrationTest.php: -------------------------------------------------------------------------------- 1 | analyzer = $this->getContainer()->get(GraphTransitAnalyzer::class); 15 | } 16 | 17 | /** 18 | * @test 19 | */ 20 | public function canRecognizeNewAddress(): void 21 | { 22 | //given 23 | $this->analyzer->addTransitBetweenAddresses(1, 1, 111, 222, new \DateTimeImmutable(), new \DateTimeImmutable()); 24 | $this->analyzer->addTransitBetweenAddresses(1, 1, 222, 333 , new \DateTimeImmutable(), new \DateTimeImmutable()); 25 | $this->analyzer->addTransitBetweenAddresses(1, 1, 333, 444, new \DateTimeImmutable(), new \DateTimeImmutable()); 26 | 27 | //when 28 | $result = $this->analyzer->analyze(1, 111); 29 | 30 | //then 31 | self::assertSame([111, 222, 333, 444], $result); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Integration/Neo4jTestCase.php: -------------------------------------------------------------------------------- 1 | getContainer()->get(ClientInterface::class)->transaction(function(TransactionInterface $tsx) { 14 | $tsx->run('MATCH (n) DETACH DELETE n'); 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/Integration/Repair/VehicleRepairAssert.php: -------------------------------------------------------------------------------- 1 | getStatus()); 18 | } else { 19 | self::assertEquals(ResolveResult::STATUS_ERROR, $result->getStatus()); 20 | } 21 | $this->result = $result; 22 | } 23 | 24 | public function free(): self 25 | { 26 | self::assertEquals(Money::zero(), $this->result->getTotalCost()); 27 | return $this; 28 | } 29 | 30 | public function allParts(array $parts): self 31 | { 32 | self::assertEquals($parts, $this->result->getAcceptedParts()); 33 | return $this; 34 | } 35 | 36 | public function by(PartyId $handlingParty): self 37 | { 38 | self::assertEquals($handlingParty->toUuid(), $this->result->getHandlingParty()); 39 | return $this; 40 | } 41 | 42 | public function allPartsBut(array $parts, array $excludedParts): self 43 | { 44 | $exptectedParts = array_filter($parts, fn(string $part) => !in_array($part, $excludedParts)); 45 | self::assertEquals($exptectedParts, $this->result->getAcceptedParts()); 46 | return $this; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /tests/Unit/Contracts/Legacy/DocumentTest.php: -------------------------------------------------------------------------------- 1 | anyUser = new User(); 20 | $this->otherUser = new User(); 21 | } 22 | 23 | /** 24 | * @test 25 | */ 26 | public function onlyDraftCanBeVerifiedByUserOtherThanCreator(): void 27 | { 28 | $doc = new Document($this->anyNumber, $this->anyUser); 29 | 30 | $doc->verifyBy($this->otherUser); 31 | 32 | self::assertEquals(DocumentStatus::VERIFIED, $doc->getStatus()); 33 | } 34 | 35 | /** 36 | * @test 37 | */ 38 | public function canNotChangePublished(): void 39 | { 40 | $doc = new Document($this->anyNumber, $this->anyUser); 41 | $doc->changeTitle($this->title); 42 | $doc->verifyBy($this->otherUser); 43 | $doc->publish(); 44 | 45 | try { 46 | $doc->changeTitle(''); 47 | } catch (\Throwable $exception) { 48 | self::assertTrue(true); 49 | } 50 | 51 | self::assertEquals($this->title, $doc->getTitle()); 52 | } 53 | 54 | /** 55 | * @test 56 | */ 57 | public function changingVerifiedMovesToDraft(): void 58 | { 59 | $doc = new Document($this->anyNumber, $this->anyUser); 60 | $doc->changeTitle($this->title); 61 | $doc->verifyBy($this->otherUser); 62 | 63 | $doc->changeTitle(''); 64 | 65 | self::assertEquals(DocumentStatus::DRAFT, $doc->getStatus()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/Unit/Contracts/Model/State/Dynamic/FakeDocumentPublisher.php: -------------------------------------------------------------------------------- 1 | events[] = $event; 15 | return $event; 16 | } 17 | 18 | public function contains(string $eventClass): void 19 | { 20 | $events = array_filter($this->events, fn($event) => get_class($event) === $eventClass); 21 | Assert::assertNotEmpty($events); 22 | } 23 | 24 | public function noEvents(): void 25 | { 26 | Assert::assertEmpty($this->events); 27 | } 28 | 29 | public function reset(): void 30 | { 31 | $this->events = []; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Unit/Entity/DriverLicenseTest.php: -------------------------------------------------------------------------------- 1 | expectException(\InvalidArgumentException::class); 16 | DriverLicense::withLicense('invalid'); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function cannotCreateEmptyLicense(): void 23 | { 24 | $this->expectException(\InvalidArgumentException::class); 25 | DriverLicense::withLicense(''); 26 | } 27 | 28 | /** 29 | * @test 30 | */ 31 | public function canCreateValidLicense(): void 32 | { 33 | //when 34 | $license = DriverLicense::withLicense('FARME100165AB5EW'); 35 | 36 | //then 37 | self::assertEquals('FARME100165AB5EW', $license->asString()); 38 | } 39 | 40 | /** 41 | * @test 42 | */ 43 | public function canCreateInvalidLicenseExplicitly(): void 44 | { 45 | //when 46 | $license = DriverLicense::withoutValidation('invalid'); 47 | 48 | //then 49 | self::assertEquals('invalid', $license->asString()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Unit/Repair/Legacy/Job/RepairTest.php: -------------------------------------------------------------------------------- 1 | setContract($this->fullCoverageWarranty()); 23 | //when 24 | $result = $employee->doJob($this->fullRepair()); 25 | //then 26 | self::assertEquals(JobResult::DECISION_ACCEPTED, $result->getDecision()); 27 | self::assertEquals(Money::zero(), $result->getParam('totalCost')); 28 | self::assertEquals($this->allParts(), $result->getParam('acceptedParts')); 29 | } 30 | 31 | private function fullRepair(): RepairJob 32 | { 33 | $job = new RepairJob(); 34 | $job->setEstimatedValue(Money::from(50000)); 35 | $job->setPartsToRepair($this->allParts()); 36 | 37 | return $job; 38 | } 39 | 40 | private function fullCoverageWarranty(): SignedContract 41 | { 42 | $contract = new SignedContract(); 43 | $contract->setCoverageRatio(100); 44 | $contract->setCoveredParts($this->allParts()); 45 | 46 | return $contract; 47 | } 48 | 49 | /** 50 | * @return string[] 51 | */ 52 | private function allParts(): array 53 | { 54 | return [Parts::SUSPENSION, Parts::PAINT, Parts::GEARBOX, Parts::ENGINE]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Unit/Repair/Legacy/Service/JobDoerTest.php: -------------------------------------------------------------------------------- 1 | repair(1, $this->repairJob()); 23 | 24 | self::assertEquals(JobResult::DECISION_ACCEPTED, $result->getDecision()); 25 | self::assertEquals(Money::zero(), $result->getParam('totalCost')); 26 | self::assertEquals($this->allParts(), $result->getParam('acceptedParts')); 27 | } 28 | 29 | private function repairJob(): RepairJob 30 | { 31 | $job = new RepairJob(); 32 | $job->setEstimatedValue(Money::from(7000)); 33 | $job->setPartsToRepair($this->allParts()); 34 | 35 | return $job; 36 | } 37 | 38 | /** 39 | * @return string[] 40 | */ 41 | private function allParts(): array 42 | { 43 | return [Parts::ENGINE, Parts::GEARBOX, Parts::PAINT, Parts::SUSPENSION]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Unit/Ride/RequestForTransitTest.php: -------------------------------------------------------------------------------- 1 | getTariff()->getName()); 22 | self::assertNotEquals(0,$requestForTransit->getTariff()->getKmRate()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Unit/Ride/TransitTest.php: -------------------------------------------------------------------------------- 1 | transit(); 21 | 22 | //expect 23 | $transit->changeDestination(Distance::ofKm(20)); 24 | 25 | //then 26 | self::assertEquals(Distance::ofKm(20), $transit->getDistance()); 27 | } 28 | 29 | /** 30 | * @test 31 | */ 32 | public function cannotChangeDestinationWhenTransitIsCompleted(): void 33 | { 34 | //given 35 | $transit = $this->transit(); 36 | //and 37 | $transit->completeAt(Distance::ofKm(20)); 38 | 39 | //then 40 | self::expectException(\RuntimeException::class); 41 | 42 | //when 43 | $transit->changeDestination(Distance::ofKm(20)); 44 | } 45 | 46 | /** 47 | * @test 48 | */ 49 | public function canCompleteTransit(): void 50 | { 51 | //given 52 | $transit = $this->transit(); 53 | 54 | //expect 55 | $transit->completeAt(Distance::ofKm(20)); 56 | 57 | //then 58 | self::assertEquals(Status::COMPLETED, $transit->getStatus()); 59 | } 60 | 61 | private function transit(): Transit 62 | { 63 | return new Transit(Tariff::ofTime(new \DateTimeImmutable()), Uuid::v4()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | bootEnv(dirname(__DIR__).'/.env'); 13 | } 14 | --------------------------------------------------------------------------------