def cal_work_experience_days(sender, instance, **kwargs): """ calculates the amount of days experience working - roughly""" all_months = [] #Get all politican work experience instances for experience in instance.politician.work.all(): # when saving a new instance it saves the object first to get and idThis makes sure that the code is only # done on instances that are fully complete. if experience.startdate: # check if they are currently getting experience or if they have an end date if experience.enddate: end = experience.enddate else: end = date.today() #Find out how many months that this period represents months = monthdelta.monthmod(experience.startdate, end) # add all months in the range to a list for month in range(0, (months[0].months + 1)): all_months.append(experience.startdate + relativedelta.relativedelta(months=+month)) # remove duplicates from list num_months = len(list_unique_order_preserving(all_months)) # calculate roughly how many days this is instance.politician.work_experience_days = num_months * (365 / 12) # save politician with newly calculated number of days instance.politician.save()
def detail(request, user_id, id): the_detail = Work.objects.get(id=id) the_goals = Goal.objects.filter(work_id=id, is_end=0) today = date.today().strftime('%Y/%m/%d') dt1 = the_detail.created_at dt2 = the_detail.deadline # 2つの日付の差を、月単位/年単位で求める ----------- # (monthdelta(20), datetime.timedelta(days=25)) <-タプル(リストみたいなもん) mmod = monthmod(dt1, dt2) ## 月数差(余りは切り捨て) months = mmod[0].months # 20 ## 年数差(余りは切り捨て)月数差を12で割ります。月の日数は色々ですが、年の月数は常に12なのでこれでok years = mmod[0].months // 12 # 1 created_at = the_detail.created_at.strftime('%Y/%m/%d') deadline = the_detail.deadline.strftime('%Y/%m/%d') data = { 'user_id': user_id, 'detail': the_detail, 'months': range(months), 'years': years, 'created_at': created_at, 'today': today, 'deadline': deadline, 'the_goals': the_goals, } return render(request, 'pages/detail.html', data)
def month_diff(d1, d2): """ return the difference between d1, d2 """ # year_diff = d2.year - d1.year # month = year_diff * 12 + abs(d1.month - d2.month) # month = month if month else 1 month_diff = monthmod(d1, d2)[0].months return month_diff if month_diff else 1
def months_diff(d1, d2, date_format='%Y-%m-%d'): """ days between d1 and d2 both included, so if d1 = d2. :param d1: date string :param d2: date string :param date_format: :return: d2 - d1 in months """ date1 = datetime.strptime(d1, date_format) date2 = datetime.strptime(d2, date_format) return monthdelta.monthmod(date1, date2)[0].months
def date_to_monthnum(date: datetime.date) -> int: """Convert a given date object to the number-of-months-since-0-AD. Why? Because that's how Heavens Above encodes it. Args: date: The date to convert. Returns: The number of months since 0 AD. """ ad1 = datetime.date(1, 1, 1) return monthdelta.monthmod(ad1, date)[0].months + 12
def months_diff(d1, d2, date_format=None): """ days between d1 and d2 both included, so if d1 = d2. :param d1: date string :param d2: date string :param date_format: :return: d2 - d1 in months """ f1 = get_date_format(d1) if date_format is None else date_format f2 = get_date_format(d2) if date_format is None else date_format if f1 is None or f2 is None: return None else: date1 = datetime.strptime(d1, f1) date2 = datetime.strptime(d2, f2) return monthdelta.monthmod(date1, date2)[0].months
def duration_str(self): monthdelta, timedelta = monthmod(self.first_visit(), timezone.now().date()) months = monthdelta.months days = timedelta.days years = months/12 month_remainder = months - (years * 12) retval = "" if months < 12: retval ="%d months" % months else: if years == 1 : retval = "1 year" elif years > 1: retval = "%d years" % years if month_remainder > 1: retval += " and %d months" % month_remainder return retval
def is_reminder_required(record, months_to_notify): """ Input record: A dictionary of key rcid to value of a subset of fields from the redcap db Output boolean: True/False based on satisfying the condition to email """ try: if record['lstremndr_dt'] == datetime.today().date().strftime( '%Y/%m/%d'): print 'already sent' return False, 0 if record['testing_reminder'] != u'1': # if the patient had opted out of reminders Don't bother further condtions return False, 0 elif (record['rapid1'] is u'1' and record['rapid2'] is not u'0') or record['dhiv'] is u'P': # The above condition is true for positive patients # Since positive patients will be going through # a different treatment plan, they dont need to # be reminded for testing. So return false # The values meani, 0-Negative 1-Positive 2-Indeterminate return False, 0 else: current_date = datetime.today() visit_date = datetime.strptime(record['visit_date'], "%Y-%m-%d") year = visit_date.year current_year = current_date.year limit_year = current_year - 2 print(limit_year) if year < limit_year: return False, 0 month, days = monthmod(visit_date, current_date) # Now, if record has a visit date that falls in to you notification slab, # Then this email Id should be notified if month.months >= months_to_notify: if visit_date.month in companion_months(current_date.month): return True, month.months except: #Ignore if we don't have the user information for the rcid log.critical(traceback.format_exc()) return False, 0
def is_reminder_required_etc(record, months_to_notify): try: current_date = datetime.today() visit_date = datetime.strptime(record['collect_date'], "%Y-%m-%d") month, days = monthmod(visit_date, current_date) # Now, if record has a visit date that falls in to you notification slab, # Then this email Id should be notified if month.months >= months_to_notify: if visit_date.month in companion_months(current_date.month): return True, month.months except: #Ignore if we don't have the user information for the rcid log.critical(traceback.format_exc()) return False, 0
def calculateTimeInRank(self,_start_date): tir = '' totalMonths = None try: if _start_date: then = dateUtils.parseUTCDateOnly(_start_date) now = dateUtils.parseUTCDateOnly(dateUtils.formatUTCDateOnly()) delta = monthdelta.monthmod(then,now) months = delta[0].months totalMonths = months if months > 11: months = totalMonths years = months/12 months -= years*12 tir = '%iy ' % (years) tir += '%im' % (months) except Exception,e: pass
def get_project_month(start_date, end_date, format=None): # 2つの日付の差を、月単位/年単位で求める ----------- mmod = monthmod(start_date, end_date) # 月数差(余りは切り捨て) month_delta = mmod[0].months + 1 # print(month_delta) project_month_list = [] if format == 'year_month': for i in range(month_delta): project_month_list.append((start_date + relativedelta(months=i)).strftime('%Y-%m-%d')[:-3]) else: for i in range(month_delta): project_month_list.append((start_date + relativedelta(months=i)).strftime('%Y-%m-%d')) # print(project_month_list) return project_month_list
def update_month_booking(sender, instance, **kwargs): ''' Обновление данных о месячной загрузке в связанной таблице MonthBooking ''' # disable the handler during fixture loading if kwargs['raw']: return # удаление старых данных MonthBooking.objects.filter(booking=instance).delete() # округление начального и конечного месяца участия в проекте до 1 числа start_month = instance.start_date.replace(day=1) end_month = instance.finish_date.replace(day=1) # месяцы, в которых нужно отразить нагрузку month_generator = ( start_month + monthdelta(i) for i in range(monthmod(start_month, end_month)[0].months + 1)) for month in month_generator: # последнее число месяца monthtail = month + monthdelta(1) - timedelta(1) start = month if instance.start_date < month else instance.start_date finish = monthtail if instance.finish_date > monthtail else instance.finish_date days = workdays(start, finish) # число запланированных рабочих дней в месяце vol = volume(days, instance.load) # трудоемкость работ в месяце load = days / workdays(month, monthtail) * instance.load # нагрузка за месяц # заполнение таблицы помесячной загрузки month_booking = MonthBooking(booking=instance, month=month, days=days, load=load, volume=vol) month_booking.save()
def cal_political_experience_days(sender, instance, **kwargs): all_months = [] #Get all politican political experience instances for experience in instance.politician.political.all(): # when saving a new instance it saves the object first to get and idThis makes sure that the code is only # done on instances that are fully complete. if experience.startdate: # check if they are currently getting experience or if they have an end date if experience.enddate: end = experience.enddate else: end = date.today() #Find out how many months that this period represents months = monthdelta.monthmod(experience.startdate, end) # add all months in the range to a list for month in range(0,(months[0].months + 1)): all_months.append(experience.startdate + relativedelta.relativedelta(months=+month)) # remove duplicates from list num_months = len(list_unique_order_preserving(all_months)) # calculate roughly how many days this is instance.politician.political_experience_days = num_months * (365 / 12) # save politician with newly calculated number of days instance.politician.save()
def _cmpt_mw_rng(self): '''Compute moving window range, number of possible windows and number of possible maximum steps per window''' win_rng = [] if self._twt == 'month': t_idx_0 = self._t_idx[0] for date in self._t_idx: win_rng.append(monthmod(t_idx_0, date)[0].months) self._ws_inc = self._ws elif self._twt == 'year': t_idx_0 = self._t_idx[0].year for date in self._t_idx: win_rng.append(date.year - t_idx_0) self._ws_inc = self._ws elif self._twt == 'range': n_wins = int(np.ceil(self._n_data_pts / self._tuss)) win_rng = np.repeat(np.arange(n_wins), self._tuss)[:self._n_data_pts] self._ws_inc = int(self._ws // self._tuss) else: raise NotImplementedError win_rng = np.array(win_rng, dtype=np.int64, order='c') win_rng.flags.writeable = False max_val = win_rng.max() assert np.all(win_rng >= 0), 'win_rng is not ascending!' assert max_val > self._ws_inc, ( 'Number of possible windows less than window_size!') unq_win_rng_vals = np.unique(win_rng) if (self._twt == 'month') or (self._twt == 'year'): mwi = unq_win_rng_vals.shape[0] - self._ws elif self._twt == 'range': mwi = unq_win_rng_vals.shape[0] - self._ws_inc self._mwr = win_rng self._mwi = mwi + 1 assert self._mwi > 1, ( 'Number of final windows cannot be less than two!') max_steps = 0 for i in range(self._mwi): ris = (self._mwr >= i) & (self._mwr < (i + self._ws_inc)) max_steps = max(max_steps, ris.sum()) max_steps = int(max_steps) assert max_steps, 'This should not happen!' self._mss = max_steps if self.verbose: print('_mwi:', self._mwi) print('_mss:', self._mss) print('_mwr:', self._mwr) print('unq_win_rng_vals:', unq_win_rng_vals) self._mw_rng_cmptd_flag = True return
def printGraph(): """ 履歴をグラフに表示 """ jdata = jsonUtils.readJsonFile(dataFile) if (jdata == {}): return 404 xl = [] yl = [] yd = [] expi = 0 # 月々のデータを取得 for row in jdata['log']: adj = 0 if ('adj' in row): adj = row['adj'] when = datetime.datetime.strptime(row['when'], '%Y-%m-%d') if (datetime.date(when.year + SHOW_YEARS, when.month, when.day) < datetime.date.today()): continue xl.append(when) yl.append(row['bank'] + row['cash'] - row['card']) # 定期出費を計上 dum = 0 for erow in jdata['exp']: ewhen = datetime.datetime.strptime(erow['when'], '%Y-%m-%d') ewhen = ewhen.date() - datetime.timedelta(days=ewhen.day - 1) ldate = when.date() - datetime.timedelta(days=when.day - 1) if ((ldate > ewhen) and (ldate <= ewhen + monthdelta.monthdelta(erow['month']))): dum += erow['exp'] * (monthdelta.monthmod( ldate, ewhen + monthdelta.monthdelta( erow['month']))[0].months) / erow['month'] yd.append(row['bank'] + row['cash'] - row['card'] + adj + dum) xe = [] ye = [] for row in [row for row in jdata['exp'] if row['month'] == 0]: when = datetime.datetime.strptime(row['when'], '%Y-%m-%d') if (datetime.date(when.year + SHOW_YEARS, when.month, when.day) < datetime.date.today()): continue xe.append(when) ye.append(row['exp']) plt.switch_backend("agg") plt.subplots(figsize=(16, 9)) plt.plot(xl, yd, label="dummy", linestyle="--") plt.plot(xl, yl, label="log") plt.scatter(xe, ye, label="expense", color="red") plt.xlabel("when") plt.ylabel("金額", fontproperties=fp) plt.legend() plt.savefig("log.png") return 0
def prev_billing_date(self, test_date=None): if not test_date: test_date = date.today() day_difference = monthmod(self.start_date, test_date)[1] return test_date - day_difference
def prev_billing_date(self, test_date=date.today()): day_difference = monthmod(self.start_date, test_date)[1] return test_date - day_difference
def get_owners_and_dates(asset_ID, request_start, request_end): # define the owners_and_dates class that will be populated and returned class Owners_And_Dates(object): slots = 0 def __init__(self, asset_id, owner_id, first_name, last_name, start_for_owner, end_for_owner, start_requested, end_requested, days_requested, days_available, days_unavailable, unavailable_date_detail, available_date_detail): self.asset_id = asset_id self.owner_id = owner_id self.start_for_owner = start_for_owner self.end_for_owner = end_for_owner self.start_requested = start_requested self.end_requested = end_requested self.first_name = first_name self.last_name = last_name self.days_requested = days_requested self.days_available = days_available self.days_unavailable = days_unavailable self.slot_id = start_requested.toordinal() self.date_span_unavailable_detail = unavailable_date_detail self.date_span_available_detail = available_date_detail Owners_And_Dates.slots += 1 def get_date_span_detail(self): # if days_available < days_requested, then the user can click to view details # this function will get the details (records from BookingDetails table) # and store them in an object if self.days_available < self.days_requested: details = BookingDetail.objects.all().filter( booking_id_id=self.asset_id, ) return {(1, 2, 3), (2, 3, 4)} def owner_display_name(self): return "%s %s" % (self.first_name, self.last_name) def numberOfSlots(self): return Owners_And_Dates.slots def __repr__(self): return "Owner is: %s | Start Date: %s | End Date: %s" % ( self.owner_id, self.owner_display_name, self.start_for_owner, self.end_for_owner) def __str__(self): return "Owner is: %s | Start Date: %s | End Date: %s" % ( self.owner_id, self.start_for_owner, self.end_for_owner) # empty object owners_and_dates_list = [] # empty dictionary sort_order_of_owners = {} # get the asset in question the_asset = Asset.objects.get(pk=asset_ID) # extract the variables needed for later static_start_date = the_asset.sharing_start_date slot_duration = the_asset.slot_duration_unit num_slots = int(the_asset.number_of_slot_units) # don't continue if no slots entered (will get divide by zero error) if num_slots == 0: return [] # don't continue if the requested start date is on or before the static start date! if request_start <= static_start_date: return [] # populate dictionary of the sort order and owners the_asset_mapping = Asset_User_Mapping.objects.all().filter( asset_ID=asset_ID, is_owner=True).order_by("position_in_rotation") if the_asset_mapping.count() == 0: return [] for mapping in the_asset_mapping: sort_order_of_owners[mapping.position_in_rotation] = mapping.user_ID_id # ensure the dates are python dates (may not be needed when coming from database) dateformat = '%Y-%m-%d' start_date = request_start end_date = request_end # calculate number of days requested (i.e. number of nights) requested_num_days = end_date.toordinal() - start_date.toordinal() print "%s nights" % requested_num_days # check here if there is only one Owner if the_asset_mapping.count() == 1: # check Booking table to find out the number of days un-available for the range date_ranges = check_availability(asset_ID, start_date, end_date) unavailable_details = date_ranges['unavailable'] num_days_available = requested_num_days - len(unavailable_details) num_days_unavailable = requested_num_days - num_days_available slot_owner_on_requested_start_date = sort_order_of_owners.get(1) o = User.objects.get(id=slot_owner_on_requested_start_date) available_details = date_ranges['available'] o_and_d = Owners_And_Dates( asset_ID, slot_owner_on_requested_start_date, o.first_name, o.last_name, start_date, end_date, start_date, end_date, requested_num_days, num_days_available, num_days_unavailable, unavailable_details, available_details) owners_and_dates_list.append(o_and_d) return owners_and_dates_list if str(slot_duration) == "Week": slot_duration_days = 7 elif str(slot_duration) == "Month": slot_duration_days = 30 # can't use a set number here for months else: slot_duration = 1 # calculate number of days in the owner-duration period num_days_per_period = slot_duration_days * num_slots print "Number of days per Owner: %s" % num_days_per_period if str(slot_duration) == "Week": # get the number of slots from static start date to requested start date # in order to work out the number of slots since static start date delta = start_date - static_start_date num_duration_periods_from_static_to_start = delta.days / slot_duration_days num_slot_periods_in_static_to_start = num_duration_periods_from_static_to_start / num_slots print "%s is the number of %s since static start date" % ( num_duration_periods_from_static_to_start, slot_duration) print "%s is number of slots since static start date" % num_slot_periods_in_static_to_start elif str(slot_duration) == "Month": # get the number of months from static start date to requested start date # this will be the number of slots since static start date num_months = monthdelta.monthmod(static_start_date, start_date) num_duration_periods_from_static_to_start = num_months[0].months num_slot_periods_in_static_to_start = int( num_duration_periods_from_static_to_start) / num_slots print "%s is the number of %s since static start date" % ( num_duration_periods_from_static_to_start, slot_duration) print "%s is number of slots since static start date" % num_slot_periods_in_static_to_start print "%s is the days remaining" % num_months[1].days # then which slot in the rotation order is being requested remainder_of_mod = divmod(num_slot_periods_in_static_to_start, len(sort_order_of_owners)) slot_order_on_requested_start_date = remainder_of_mod[1] + 1 slot_owner_on_requested_start_date = sort_order_of_owners.get( slot_order_on_requested_start_date) print "%s is slot order on the requested start date" % slot_order_on_requested_start_date print "%s is the ID of the owner on the requested start date" % slot_owner_on_requested_start_date # requested start date is in someone's slot - get the start and end date of that slot if str(slot_duration) == "Week": slot_start_for_start_date = get_start_slot_date( static_start_date, start_date, num_days_per_period, dateformat) slot_end_for_start_date = get_end_slot_date(slot_start_for_start_date, num_days_per_period) elif str(slot_duration) == "Month": # start date will simply be day of static start day with month and year of requested start date! slot_start_for_start_date = datetime.date(start_date.year, start_date.month, static_start_date.day) # slot end will be the same date plus <num_slots> later slot_end_for_start_date = slot_start_for_start_date + monthdelta.monthdelta( num_slots) print "%s is start of slot with requested start date" % slot_start_for_start_date print "%s is end of slot with requested start date" % slot_end_for_start_date # now work out if only one owner is affected by the requested date range # or more than one owner # store Owner ID and slot start and slot end dates in object to return # first slot details delta = slot_end_for_start_date - start_date num_days_slot_1 = delta.days print "first slot delta days: %s" % num_days_slot_1 # depending on whether the last/end requested date falls before or after the slot_end, # the 'end_date' variable and days_requested variable for next functions will differ if requested_num_days < num_days_slot_1: first_slot_end_date = end_date days_requested = requested_num_days else: first_slot_end_date = slot_end_for_start_date days_requested = num_days_slot_1 print "first slot end date: %s" % first_slot_end_date print "days requested: %s" % days_requested # start date could be the very first day of a slot, # if so, move on to the next slot (don't add a record of '0' days) # check Booking table to find out the number of days un-available for the range date_ranges = check_availability(asset_ID, start_date, first_slot_end_date) unavailable_details = date_ranges['unavailable'] num_days_available = days_requested - len(unavailable_details) num_days_unavailable = days_requested - num_days_available o = User.objects.get(id=slot_owner_on_requested_start_date) available_details = date_ranges['available'] o_and_d = Owners_And_Dates( asset_ID, slot_owner_on_requested_start_date, o.first_name, o.last_name, slot_start_for_start_date, slot_end_for_start_date, start_date, first_slot_end_date, days_requested, num_days_available, num_days_unavailable, unavailable_details, available_details) owners_and_dates_list.append(o_and_d) # if the number from subtracting requested end date from the slot end date is >=0 # then there is no need to move into the next slot i.e. only one owner should be returned delta = slot_end_for_start_date - end_date print "delta: %s" % delta if delta.days >= 0: print "delta days: %s" % delta.days print "Stays in first slot" slots_affected = 1 else: print "delta days: %s" % delta.days print "Goes to second slot" print "%s days required from owner slot 1: " % num_days_slot_1, slot_owner_on_requested_start_date days_to_cover = requested_num_days - num_days_slot_1 print "%s days left to cover" % days_to_cover end_of_previous_slot = slot_end_for_start_date # use this to check rows/items to return slots_affected = 1 previous_slot_order = slot_order_on_requested_start_date while days_to_cover > 0: # get date info for next slot next_slot_start_date = end_of_previous_slot if str(slot_duration) == "Week": next_slot_end = get_end_slot_date(next_slot_start_date, num_days_per_period) elif str(slot_duration) == "Month": next_slot_end = next_slot_start_date + monthdelta.monthdelta( num_slots) # get sort order of next slot if (previous_slot_order + 1) > len(sort_order_of_owners): next_slot_order = 1 else: next_slot_order = previous_slot_order + 1 # get slot owner of next slot next_slot_owner = sort_order_of_owners.get(next_slot_order) # increment slots_affected += 1 # check if remainder days is more than one duration period # works for weeks (always 7 days), but for months, need to calculate the ACTUAL number of days # in the next period (this will vary with different length months) if str(slot_duration) == "Month": num_days_in_next_period = next_slot_end.toordinal( ) - next_slot_start_date.toordinal() # store in this variable for ease of code num_days_per_period = num_days_in_next_period if days_to_cover > num_days_per_period: # this is not the last time in loop because there are more days to cover # reset to remainder for next time in loop days_to_cover = days_to_cover - num_days_per_period end_of_previous_slot = next_slot_end previous_slot_order = next_slot_order print "%s is owner ORDER" % next_slot_order print "%s days required from next owner with ID: %s" % ( num_days_per_period, next_slot_owner) print "%s is start of next slot" % next_slot_start_date print "%s is end of next slot" % next_slot_end print "%s days still to cover" % days_to_cover date_ranges = check_availability(asset_ID, next_slot_start_date, next_slot_end) unavailable_details = date_ranges['unavailable'] num_days_available = num_days_per_period - len( unavailable_details) num_days_unavailable = num_days_per_period - num_days_available o = User.objects.get(id=next_slot_owner) available_details = date_ranges['available'] o_and_d = Owners_And_Dates( asset_ID, next_slot_owner, o.first_name, o.last_name, next_slot_start_date, next_slot_end, next_slot_start_date, next_slot_end, num_days_per_period, num_days_available, num_days_unavailable, unavailable_details, available_details) owners_and_dates_list.append(o_and_d) else: # finally, we have come to the end because remaining days to cover is less than one duration period delta = end_date - next_slot_start_date days_of_current_slot = delta.days print "%s is owner ORDER" % next_slot_order print "%s days required until end of the booking" % days_of_current_slot print "%s days required from next (final) owner with ID: %s" % ( days_of_current_slot, next_slot_owner) print "%s is start of next (final) slot" % next_slot_start_date print "%s is end of next (final) slot" % next_slot_end date_ranges = check_availability(asset_ID, next_slot_start_date, end_date) unavailable_details = date_ranges['unavailable'] num_days_available = days_of_current_slot - len( unavailable_details) num_days_unavailable = days_of_current_slot - num_days_available o = User.objects.get(id=next_slot_owner) available_details = date_ranges['available'] o_and_d = Owners_And_Dates( asset_ID, next_slot_owner, o.first_name, o.last_name, next_slot_start_date, next_slot_end, next_slot_start_date, end_date, days_of_current_slot, num_days_available, num_days_unavailable, unavailable_details, available_details) owners_and_dates_list.append(o_and_d) # make sure to set to zero to end the loop days_to_cover = 0 print "%s days still to cover" % days_to_cover # return "owners and dates: %s | slots affected: %s" % (owners_and_dates_list,slots_affected) return owners_and_dates_list
def bill(self): """ Issue a bill for a vendor. This method is called periodically by an external tool. It let all duties bill up and the executes all price modifiers such as discount encounters. We have to bill the vendor at the time of call. Only when the billing happened already today (or in the future) we will ignore that call. :rvalue:`tariff.Bill` the created bill (invoice) """ if self.last_billed >= timezone.now().date(): return Bill.objects.filter(period_end=timezone.now().date()).get() if self.next_billing > timezone.now().date(): raise ValueError( "Can not bill before the end of billing period - close it instead." ) contractor = defaults.vendor() bill = Bill.objects.create( vendor=self.vendor, logo=contractor.logo.path if contractor.logo else None, subscriber=self.vendor.address, period_start=self.last_billed, period_end=self.next_billing, contractor=contractor.address, contractor_bank=contractor.bank_account) months, rest = monthmod(self.last_billed, self.next_billing) # tranform date to datetime for billing useng StatisticsManager last_billed = datetime(self.last_billed.year, self.last_billed.month, self.last_billed.day) # bill by months (because of Discounts and better visibility on the bill) for month in range(months.months): total = Decimal("0.00") for tariff, price in Statistics.objects.bill( self.vendor, last_billed + monthdelta(month), last_billed + monthdelta(month + 1)): bill.add_item(tariff, price, settings.TAX) total += price discount = Discount.objects.cut_the_price(self.vendor, total) if discount is not None: bill.add_item(*discount) # bill the remaining time (if there is some) if rest.days > 1: total = Decimal("0.00") for tariff, price in Statistics.objects.bill( self.vendor, last_billed + months, last_billed + months + rest): bill.add_item(tariff, price, settings.TAX) total += price discount = Discount.objects.cut_the_price(self.vendor, total) if discount is not None: bill.add_item(*discount) if bill.total < 0: bill.add_item(_("Rounding price to zero"), -1 * bill.total) self.last_billed = self.next_billing self.save() # periods change is taken care of in .save() method bill.save() bill.send() return bill