def test_deliverable_burned_cost(statistics_api, put_project, post_issue, post_timesheet, post_deliverable, put_link): project = create_project() put_project(project) bound_deliverable = post_deliverable(project.project_id, create_deliverable()) # Issue with default project currency bound_issue = post_issue(project.project_id, create_issue()) put_link(bound_deliverable.object_id, bound_issue.object_id) post_timesheet(bound_issue.object_id, create_timesheet(duration=Decimal(2))) post_timesheet(bound_issue.object_id, create_timesheet(duration=Decimal(1))) post_timesheet(bound_issue.object_id, create_timesheet(duration=Decimal(1))) # Issue with overriden currency bound_issue = post_issue( project.project_id, create_issue(hour_rate=Money(Decimal(1000), currency=Currency.czk))) put_link(bound_deliverable.object_id, bound_issue.object_id) post_timesheet(bound_issue.object_id, create_timesheet(duration=Decimal(10))) # Issue without timesheet bound_issue = post_issue(project.project_id, create_issue()) put_link(bound_deliverable.object_id, bound_issue.object_id) statistics = statistics_api.get_deliverable_statistics( bound_deliverable.object_id) assert statistics.burned_cost == Money(Decimal(12402), Currency.czk)
def test_deliverable_estimated_cost(statistics_api, put_project, post_issue, put_link, post_deliverable): project = create_project() put_project(project) bound_deliverable = post_deliverable(project.project_id, create_deliverable()) # Issue with default project currency bound_issue = post_issue(project.project_id, create_issue(estimated_duration=Decimal(5))) put_link(bound_deliverable.object_id, bound_issue.object_id) # Issue with overriden currency bound_issue = post_issue( project.project_id, create_issue(estimated_duration=Decimal(2), hour_rate=Money(amount=Decimal(1000), currency=Currency.czk))) put_link(bound_deliverable.object_id, bound_issue.object_id) # Issue without estimated duration bound_issue = post_issue(project.project_id, create_issue(estimated_duration=None)) put_link(bound_deliverable.object_id, bound_issue.object_id) statistics = statistics_api.get_deliverable_statistics( bound_deliverable.object_id) assert statistics.estimated_cost == Money(amount=Decimal("5002.5"), currency=Currency.czk)
def test_issue_expenditures_cost( statistics_api, put_project, post_issue, post_expenditure, ): project = create_project() put_project(project) bound_issue = post_issue(project.project_id, create_issue(estimated_duration=Decimal(5))) post_expenditure( bound_issue.object_id, create_expenditure(cost=Money(Decimal(10), currency=Currency.czk))) post_expenditure(bound_issue.object_id, create_expenditure(status=ExpenditureStatus.submitted)) post_expenditure(bound_issue.object_id, create_expenditure(status=ExpenditureStatus.refund)) post_expenditure( bound_issue.object_id, create_expenditure(cost=Money(Decimal(100), currency=Currency.czk))) statistics = statistics_api.get_entity_statistics(bound_issue.object_id) assert statistics.burned_expenditures_cost == Money( Decimal(110), Currency.czk)
def test_issue_burned_cost_overriden_currency(statistics_api, put_project, post_issue, post_timesheet, post_deliverable, put_link): project = create_project() put_project(project) # Issue with overriden currency bound_issue = post_issue( project.project_id, create_issue(hour_rate=Money(Decimal(1000), currency=Currency.czk))) post_timesheet(bound_issue.object_id, create_timesheet(duration=Decimal(10))) statistics = statistics_api.get_entity_statistics(bound_issue.object_id) assert statistics.burned_cost == Money(Decimal(10000), Currency.czk)
def _compute_expenditures_cost( entity_statistics: List[EntityStatistics]) -> Money: return Money( amount=sum(statistic.expenditure_costs.amount for statistic in entity_statistics), currency=Currency.czk, )
def get_entity_statistics(self, entity_ids: List[EntityId]): if not entity_ids: return [] assert len({entity_id.project_id for entity_id in entity_ids}) == 1 full_ids = [entity_id.full_id for entity_id in entity_ids] # All entities are from same project default_hour_rate = self._get_project_hour_rate(entity_ids[0].project_id) entity_data = self._get_entity_data(full_ids) burned_time = self._get_burned_time(full_ids) expenditure_costs = self._get_expenditure_costs(full_ids) entity_statistics = [] for entity_id in entity_ids: hour_rate = default_hour_rate if entity_data[entity_id].hour_rate: hour_rate = entity_data[entity_id].hour_rate entity_statistics.append( EntityStatistics( estimated_duration=entity_data[entity_id].estimated_duration, hour_rate=hour_rate, burned_duration=burned_time.get(entity_id, Decimal(0)), expenditure_costs=expenditure_costs.get( entity_id, Money( amount=Decimal(0), currency=Currency.czk, ) ), ) ) return entity_statistics
def _compute_burned_cost( entity_statistics: List[EntityStatistics]) -> Money: return Money( amount=sum(statistic.burned_duration * statistic.hour_rate.amount for statistic in entity_statistics), currency=Currency.czk, )
def _row_to_entity(row) -> BoundIssue: hour_rate = None if row["hour_rate_amount"] and row["hour_rate_currency"]: hour_rate = Money( amount=row["hour_rate_amount"], currency=Currency(row["hour_rate_currency"]), ) return BoundIssue(object_id=EntityId(row["object_id"]), issue=Issue( name=row["name"], status=IssueStatus(row["status"]), type_=IssueType(row["type"]), priority=IssuePriority(row["priority"]), date_opened=row["date_opened"], date_closed=row["date_closed"], deadline=row["deadline"], description=row["description"], external_type=row["external_type"], estimated_duration=row["estimated_duration"], hour_rate=hour_rate, files=[], links=[], tags=[], tasks=[], ))
def test_project_expenditures_cost_no_entities(statistics_api, put_project): project = create_project() put_project(project) statistics = statistics_api.get_project_statistics(project.project_id) assert statistics.burned_expenditures_cost == Money( Decimal(0), Currency.czk)
def _get_entity_data(self, entity_ids: List[str]): query = select( [ ISSUES_TABLE.c.hour_rate_amount, ISSUES_TABLE.c.hour_rate_currency, ISSUES_TABLE.c.estimated_duration, ISSUES_TABLE.c.object_id, ] ).where( ISSUES_TABLE.c.object_id.in_(entity_ids) ) result = self._session.execute(query) entity_data = {} for row in result: amount = row["hour_rate_amount"] currency = row["hour_rate_currency"] hour_rate = None if amount is not None and currency: hour_rate = Money( amount=amount, currency=currency, ) entity_data[EntityId(row["object_id"])] = EntityStatisticsData( hour_rate=hour_rate, estimated_duration=row["estimated_duration"] ) return entity_data
def test_project_estimated_cost(statistics_api, put_project, post_issue): project = create_project() put_project(project) # Issue with default project currency post_issue(project.project_id, create_issue(estimated_duration=Decimal(5))) # Issue with overriden currency post_issue( project.project_id, create_issue(estimated_duration=Decimal(2), hour_rate=Money(amount=Decimal(1000), currency=Currency.czk))) # Issue without estimated duration post_issue(project.project_id, create_issue(estimated_duration=None)) statistics = statistics_api.get_project_statistics(project.project_id) assert statistics.estimated_cost == Money(amount=Decimal("5002.5"), currency=Currency.czk)
def test_issue_estimated_cost_overriden_currency( statistics_api, put_project, post_issue, ): project = create_project() put_project(project) # Issue with overriden currency bound_issue = post_issue( project.project_id, create_issue(estimated_duration=Decimal(2), hour_rate=Money(amount=Decimal(1000), currency=Currency.czk))) statistics = statistics_api.get_entity_statistics(bound_issue.object_id) assert statistics.estimated_cost == Money(amount=Decimal("2000"), currency=Currency.czk)
def test_issue_burned_cost_no_timesheets( statistics_api, put_project, post_issue, ): project = create_project() put_project(project) # Issue without timesheet bound_issue = post_issue(project.project_id, create_issue()) statistics = statistics_api.get_entity_statistics(bound_issue.object_id) assert statistics.burned_cost == Money(Decimal(0), Currency.czk)
def _get_project_hour_rate(self, project_id: str): query = select( [ PROJECTS_TABLE.c.hour_rate_amount, PROJECTS_TABLE.c.hour_rate_currency, ] ).where( PROJECTS_TABLE.c.project_id == project_id ) row = self._session.execute(query).fetchone() return Money( amount=row["hour_rate_amount"], currency=Currency(row["hour_rate_currency"]), )
def test_issue_expenditures_cost_no_expenditures( statistics_api, put_project, post_issue, ): project = create_project() put_project(project) bound_issue = post_issue(project.project_id, create_issue(estimated_duration=Decimal(5))) statistics = statistics_api.get_entity_statistics(bound_issue.object_id) assert statistics.burned_expenditures_cost == Money( Decimal(0), Currency.czk)
def test_issue_estimated_cost_default_currency( statistics_api, put_project, post_issue, ): project = create_project() put_project(project) # Issue with default project currency bound_issue = post_issue(project.project_id, create_issue(estimated_duration=Decimal(5))) statistics = statistics_api.get_entity_statistics(bound_issue.object_id) assert statistics.estimated_cost == Money(amount=Decimal("3002.5"), currency=Currency.czk)
def test_issue_estimated_cost_no_estimate( statistics_api, put_project, post_issue, ): project = create_project() put_project(project) # Issue without estimated duration bound_issue = post_issue(project.project_id, create_issue(estimated_duration=None)) statistics = statistics_api.get_entity_statistics(bound_issue.object_id) assert statistics.estimated_cost == Money(amount=Decimal(0), currency=Currency.czk)
def _row_to_cost(row): return BoundExpenditure( simple_id=SimpleId(SimpleEntityType.expenditure, row["id"]), expenditure=Expenditure(name=row["name"], description=row["description"], status=ExpenditureStatus(row["status"]), type_=ExpenditureType(row["type"]), date_opened=row["date_opened"], date_closed=row["date_closed"], deadline=row["deadline"], files=[], cost=Money( amount=row["cost_amount"], currency=Currency( row["cost_currency"]), )))
def _row_to_entity(row) -> Project: return Project( project_id=EntityId(row["project_id"]), name=row["name"], status=ProjectStatus(row["status"]), date_opened=row["date_opened"], date_closed=row["date_closed"], deadline=row["deadline"], hour_rate=Money( amount=row["hour_rate_amount"], currency=Currency(row["hour_rate_currency"]), ), description=row["description"], limitations_and_restrictions=row["limitations_and_restrictions"], goals_and_metrics=row["goals_and_metrics"], primary_color=row["primary_color"], secondary_color=row["secondary_color"], files=[])
def test_issue_burned_cost_default_currency( statistics_api, put_project, post_issue, post_timesheet, ): project = create_project() put_project(project) # Issue with default project currency bound_issue = post_issue(project.project_id, create_issue()) post_timesheet(bound_issue.object_id, create_timesheet(duration=Decimal(2))) post_timesheet(bound_issue.object_id, create_timesheet(duration=Decimal(1))) post_timesheet(bound_issue.object_id, create_timesheet(duration=Decimal(1))) statistics = statistics_api.get_entity_statistics(bound_issue.object_id) assert statistics.burned_cost == Money(Decimal(2402), Currency.czk)
def _get_expenditure_costs(self, entity_ids: List[str]): query = select( [ EXPENDITURES_TABLE.c.parent_id, func.sum(EXPENDITURES_TABLE.c.cost_amount).label("expenditure_costs"), ] ).group_by( EXPENDITURES_TABLE.c.parent_id ).where( EXPENDITURES_TABLE.c.parent_id.in_(entity_ids) ).where( EXPENDITURES_TABLE.c.status == ExpenditureStatus.approved.value ) result = self._session.execute(query) return { EntityId(row["parent_id"]): Money( amount=row["expenditure_costs"], currency=Currency.czk, ) for row in result }
def deserialize_money(money: dict) -> Money: return Money( amount=Decimal(money["amount"]), currency=Currency(money["currency"]), )
def create_money(amount: Decimal = Decimal("600.50"), currency: Currency = Currency.czk): return Money( amount=amount, currency=currency, )