def add_permit_lines(self): from regional.restrictions import GetMatchingPRsByZip if not self.agreement.system_address.zip: return property_type = 'commercial' if self.agreement.floorplan == 'Business' else 'residential' prs = GetMatchingPRsByZip(self.agreement.system_address.zip, property_type, asof=datetime.now()) print "PRs found: %r" % prs permits = [pr for pr in prs if pr.permit_fee or pr.addendum_fee] # These are the lines we need permits for. We're going to cheat # our faces off and use the note column for this. for pr in permits: # reclaim or create a permit line. pnote = ';'.join([pr.override_type or pr.region_type, ', '.join(pr.override_name or pr.region_name)]) pline = self.reclaim_line(code='PERMIT', line_type='PERMIT', note=pnote) if not pline: pline = InvoiceLine(agreement=self.agreement, line_type='PERMIT') pline.update_permit(pr, permit_product=self.products['PERMIT']) pline.save() # Put the permit line onto the final part. self.final_lines.append(pline)
def sync_all_children(self): # First, unclaim any child lines we've claimed in a previous call to this function. They'll get reclaimed in this function if necessary. self.unclaimed_lines = list(set(self.unclaimed_lines + [line for line in self.final_lines if not line.traded and line.parent])) # This is a copy of self.final_lines, filtered down to not include child lines (except trades) # It's used to detect differences at the end from self.final_lines, and will get assigned there at the end. final_lines = [line for line in self.final_lines if line.traded or not line.parent] # This function can be called more than once in a row, which is necessary if more lines get added as a result # of child lines being added. (You add a smoke package, it has a smoke detector, which requires the smoke service, # hypothetically has a smoke permit kit or something.) # A queue of lines that need their children synced. Starts with final_lines which is the top and traded lines. lines_to_sync = list(final_lines) pos = 0 while pos < len(lines_to_sync): # walk through each line and sync its children. As children get added, they'll be queued onto # the end of this loop, so they can get their nested children. line = lines_to_sync[pos] pos += 1 prod = self.products.get(line.code) if not prod: self.errors.append("Could not find product %r to sync child lines on line %r" % (line.code, line.pk)) continue # Index the contents of this line's product. contents = self.by_code(self.product_contents.get(prod.code, [])) # for every product in that product: for code, pc in contents.iteritems(): child = self.reclaim_line(parent_id=line.pk, code=code, line_type='CHILD') if not child: # If I wasn't able to reclaim an existing child, we need to make a new one for this agreement/line: child = InvoiceLine(agreement=self.agreement, parent=line) # sync the child line with the product (gets updated quantity, product type, category, etc.) child.update_child(product=self.products[code], pc=pc, parent_line=line) # Save this child. It's either been updated or is new. child.save() # Queue up this line to be synced for ITS children lines_to_sync.append(child) # and add it to final_lines final_lines.append(child) # Did the final set of lines change from what we had before? changed = set(final_lines) ^ set(self.final_lines) # And then, assign the final set of children. self.final_lines = final_lines return bool(changed)
def add_mandatory_product(self, code, quantity): # The function needs to check to see if it is already satisfied before # calling this. This function will always reclaim or create a new line # with the specified quantity. this is because add_mandatory_items # gets called multiple times, but does NOT unclaim everything like # sync_all_children does, because it may be that the mandatory items # themselves require more mandatory items. (Ex: for some reason, # businesses in california MUST have a smoke detector.) mp = self.reclaim_line(code=code, line_type='MANDATORY') if not mp: mp = InvoiceLine(agreement=self.agreement) product = self.products.get(code) if not product: self.errors.append('Could not add mandatory product %r' % code) return price = self.prices.get(code) if not price: self.errors.append( 'Mandatory product %r has no price. Campaign=%r' % (code, self.agreement.campaign_id)) mp.update_mandatory(quantity=quantity, product=product, price=price, pricedate=self.agreement.pricedate) mp.quantity = quantity mp.save() self.final_lines.append(mp)
def add_permit_lines(self): from regional.restrictions import GetMatchingPRsByZip if not self.agreement.system_address.zip: return property_type = 'commercial' if self.agreement.floorplan == 'Business' else 'residential' prs = GetMatchingPRsByZip(self.agreement.system_address.zip, property_type, asof=datetime.now()) print "PRs found: %r" % prs permits = [pr for pr in prs if pr.permit_fee or pr.addendum_fee] # These are the lines we need permits for. We're going to cheat # our faces off and use the note column for this. for pr in permits: # reclaim or create a permit line. pnote = ';'.join([ pr.override_type or pr.region_type, ', '.join(pr.override_name or pr.region_name) ]) pline = self.reclaim_line(code='PERMIT', line_type='PERMIT', note=pnote) if not pline: pline = InvoiceLine(agreement=self.agreement, line_type='PERMIT') pline.update_permit(pr, permit_product=self.products['PERMIT']) pline.save() # Put the permit line onto the final part. self.final_lines.append(pline)
def add_mandatory_product(self, code, quantity): # The function needs to check to see if it is already satisfied before # calling this. This function will always reclaim or create a new line # with the specified quantity. this is because add_mandatory_items # gets called multiple times, but does NOT unclaim everything like # sync_all_children does, because it may be that the mandatory items # themselves require more mandatory items. (Ex: for some reason, # businesses in california MUST have a smoke detector.) mp = self.reclaim_line(code=code, line_type='MANDATORY') if not mp: mp = InvoiceLine(agreement=self.agreement) product = self.products.get(code) if not product: self.errors.append('Could not add mandatory product %r' % code) return price = self.prices.get(code) if not price: self.errors.append('Mandatory product %r has no price. Campaign=%r' % (code, self.agreement.campaign_id)) mp.update_mandatory(quantity=quantity, product=product, price=price, pricedate=self.agreement.pricedate) mp.quantity = quantity mp.save() self.final_lines.append(mp)
def Warehouse(): china_cur = ChinaCursor() china_cur.SetCrossServerFlags() sql = ''' SELECT cd.agreement_id, blob, date_created, date_modified as date_modified FROM contract_details cd LEFT JOIN contracts c on c.agreement_id = cd.agreement_id ''' china_cur.execute(sql) campaigns = {c.pk: c for c in Campaign.objects.all()} existing = list(Agreement.objects.all().values_list('id', flat=True)) while True: merchandise = china_cur.fetch_many() for m in merchandise: if m['agreement_id'] in existing: continue blob = loads(m['blob']) blob['date_created'] = m['date_created'] blob['date_modified'] = m['date_modified'] agreement = Agreement() agreement.id = m['agreement_id'] blob_campaign = blob['campaign_id'].upper() campaign = campaigns.get(blob_campaign) campaign_id = None if not campaign: cur = GladosCursor() sql = ''' SELECT campaign_id, name FROM campaigns WHERE campaign_id = ? ''' cur.execute(sql, [blob_campaign]) prev_campaign = cur.fetchone() if not prev_campaign: new_campaign = Campaign() new_campaign.campaign_id = blob_campaign new_campaign.name = blob_campaign new_campaign.save() campaign_id = new_campaign.pk else: new_campaign = Campaign() new_campaign.campaign_id = blob_campaign new_campaign.name = prev_campaign.name new_campaign.save() campaign_id = new_campaign.pk else: campaign_id = campaign.campaign_id agreement.campaign_id = campaign_id applicant_id = None if 'applicant' in blob.keys(): if not blob['applicant']: first_name = '' last_name = '' initial = '' phone = '' applicant_id = Customer(first_name, last_name, initial, phone) else: first_name = blob['applicant']['first_name'] last_name = blob['applicant']['last_name'] initial = blob['applicant']['middle_initial'] phone = None if not blob['phone1']: phone = '' else: phone = blob['phone1'] applicant_id = Customer(first_name, last_name, initial, phone) else: first_name = '' last_name = '' initial = '' phone = '' applicant_id = Customer(first_name, last_name, initial, phone) agreement.applicant_id = applicant_id agreement.coapplicant = None if 'coapplicant' in blob.keys(): if not blob['coapplicant']: agreement.coapplicant_id = None else: first_name = blob['coapplicant']['first_name'] last_name = blob['coapplicant']['last_name'] initial = blob['coapplicant']['middle_initial'] phone = blob['phone1'] create_coapplicant = Customer(first_name, last_name, initial, phone) agreement.coapplicant_id = create_coapplicant billing_address_id = None if 'address' in blob.keys(): if not blob['address']: address = '' city = '' state = '' zipcode = '' country = '' billing_address_id = Location(address, city, state, zipcode, country) else: address = blob['address']['address1'] city = blob['address']['city'] state = blob['address']['state'] zipcode = blob['address']['zipcode'] country = 'US' billing_address_id = Location(address, city, state, zipcode, country) else: address = '' city = '' state = '' zipcode = '' country = '' billing_address_id = Location(address, city, state, zipcode, country) agreement.billing_address_id = billing_address_id agreement.system_address_id = billing_address_id # going to use this date again, so just going to create it first before assignment pricetable_date = blob['date_created'] agreement.pricetable_date = pricetable_date or datetime(1900, 1, 1, 0, 0) agreement.email = blob['email'] approved = None if blob['credit_status'] == 'APPROVED DCS': approved = 'DCS' else: approved = blob['credit_status'] agreement.approved = approved agreement.package_id = None if 'package' in blob.keys(): if not blob['package']: agreement.package_id = '' else: selected_package = blob['package']['code'].lower() package = Package.objects.get(code=selected_package) agreement.package_id = package.pk agreement.shipping = None if 'shipping' in blob.keys(): if not blob['shipping']: agreement.shipping = '' else: agreement.shipping = blob['shipping']['code'] else: agreement.shipping = '' agreement.monitoring = None if 'monitoring' in blob.keys(): if not blob['monitoring']: agreement.monitoring = '' else: agreement.monitoring = blob['monitoring']['code'] else: agreement.monitoring = '' agreement.floorplan = blob['floorplan'] agreement.promo_code = '' # since these are existing records, we will assume that these things have been done agreement.done_premium = '1' agreement.done_combo = '1' agreement.done_alacarte = '1' agreement.done_closing = '1' agreement.done_package = '1' agreement.done_promos = '1' agreement.save() agreement_object = Agreement.objects.get(id=m['agreement_id']) if 'package' in blob.keys(): package = blob['package'] if package: invoice = InvoiceLine() invoice.agreement = agreement_object invoice.note = '' invoice.product = package['code'] invoice.pricetable = blob['pricetable'] invoice.quantity = '1' invoice.upfront_each = package['upfront_price'] invoice.upfront_total = package['upfront_price'] invoice.upfront_strike = package['retail_price'] invoice.monthly_each = package['monthly_price'] invoice.monthly_total = package['monthly_price'] invoice.save() if 'monitoring' in blob.keys(): monitoring = blob['monitoring'] if monitoring: invoice = InvoiceLine() invoice.agreement = agreement_object invoice.note = '' invoice.product = monitoring['code'] invoice.pricetable = blob['pricetable'] invoice.quantity = '1' invoice.pricedate = pricetable_date invoice.upfront_each = monitoring['upfront_price'] invoice.upfront_total = monitoring['upfront_price'] invoice.upfront_strike = monitoring['retail_price'] invoice.monthly_each = monitoring['monthly_price'] invoice.monthly_total = monitoring['monthly_price'] invoice.save() equipment = blob['equipment'] if equipment: for e in equipment: invoice = InvoiceLine() invoice.agreement = agreement_object # there is a reason in the blob, perhaps note needs to be changed to reason? invoice.note = e['reason'] invoice.product = e['part'] invoice.pricetable = blob['pricetable'] invoice.quantity = e['quantity'] invoice.pricedate = pricetable_date invoice.upfront_each = None if e['upfront_price'] == 0: invoice.upfront_each = e['upfront_price'] else: invoice.upfront_each = e['upfront_price'] invoice.upfront_total = float(invoice.upfront_each) * invoice.quantity invoice.monthly_each = None if e['monthly_price'] == 0: invoice.monthly_each = e['monthly_price'] else: invoice.monthly_each = e['monthly_price'] invoice.monthly_total = float(invoice.monthly_each) * invoice.quantity invoice.save() if 'shipping' in blob.keys(): shipping = blob['shipping'] if shipping: invoice = InvoiceLine() invoice.agreement = agreement_object invoice.note = '' invoice.product = shipping['code'] invoice.pricetable = blob['pricetable'] invoice.quantity = '1' invoice.pricedate = pricetable_date invoice.upfront_each = shipping['upfront_price'] invoice.upfront_total = shipping['upfront_price'] invoice.save() services = blob['services'] if services: for s in services: invoice = InvoiceLine() invoice.agreement = agreement_object invoice.note = '' invoice.product = s['service'] invoice.pricetable = blob['pricetable'] invoice.quantity = '1' invoice.pricedate = pricetable_date invoice.upfront_price = s['upfront_price'] invoice.upfront_total = s['upfront_price'] invoice.upfront_strike = s['retail_price'] invoice.monthly_price = s['monthly_price'] invoice.monthly_total = s['monthly_price'] invoice.save() return
def sync_all_children(self): # First, unclaim any child lines we've claimed in a previous call to this function. They'll get reclaimed in this function if necessary. self.unclaimed_lines = list( set(self.unclaimed_lines + [ line for line in self.final_lines if not line.traded and line.parent ])) # This is a copy of self.final_lines, filtered down to not include child lines (except trades) # It's used to detect differences at the end from self.final_lines, and will get assigned there at the end. final_lines = [ line for line in self.final_lines if line.traded or not line.parent ] # This function can be called more than once in a row, which is necessary if more lines get added as a result # of child lines being added. (You add a smoke package, it has a smoke detector, which requires the smoke service, # hypothetically has a smoke permit kit or something.) # A queue of lines that need their children synced. Starts with final_lines which is the top and traded lines. lines_to_sync = list(final_lines) pos = 0 while pos < len(lines_to_sync): # walk through each line and sync its children. As children get added, they'll be queued onto # the end of this loop, so they can get their nested children. line = lines_to_sync[pos] pos += 1 prod = self.products.get(line.code) if not prod: self.errors.append( "Could not find product %r to sync child lines on line %r" % (line.code, line.pk)) continue # Index the contents of this line's product. contents = self.by_code(self.product_contents.get(prod.code, [])) # for every product in that product: for code, pc in contents.iteritems(): child = self.reclaim_line(parent_id=line.pk, code=code, line_type='CHILD') if not child: # If I wasn't able to reclaim an existing child, we need to make a new one for this agreement/line: child = InvoiceLine(agreement=self.agreement, parent=line) # sync the child line with the product (gets updated quantity, product type, category, etc.) child.update_child(product=self.products[code], pc=pc, parent_line=line) # Save this child. It's either been updated or is new. child.save() # Queue up this line to be synced for ITS children lines_to_sync.append(child) # and add it to final_lines final_lines.append(child) # Did the final set of lines change from what we had before? changed = set(final_lines) ^ set(self.final_lines) # And then, assign the final set of children. self.final_lines = final_lines return bool(changed)
def update_invoice_lines(self): # The incoming invoice lines should look like: ''' [{ 'code': 'COPPER', 'quantity': 1 'traded': true // or not present. }, ...] ''' # coerce the incoming invoice lines: incoming_lines = [ IL(line, updater=self) for line in self.blob['invoice_lines'] ] # If the IL constructor put any errors in, stop now. if self.errors: return # Next get every existing invoice line. self.existing_lines = list(self.agreement.invoice_lines.all()) self.unclaimed_lines = list( self.existing_lines ) # Unclaim all existing lines. We may reclaim them soon. # this is the list of lines that will be on the agreement at the end: self.final_lines = [] for il in list(incoming_lines): if not il.price: incoming_lines.remove(il) self.messages.append( '%s removed from the agreement because it is not available.' % il.code) continue available = getattr(il.price, 'available_mask', None) if available is None: available = il.price.available if not available: incoming_lines.remove(il) self.messages.append( '%s removed from the agreement because it is no longer available.' % il.code) continue # First, do every line that came in from the system NOT traded. # Invoice Lines for these should all be TOP. # (This includes package, monitoring, alacarte, but not children, mandatory services...) for il in incoming_lines: if not il.line_type == 'TOP': continue # il is a fakey line. line = self.reclaim_line(code=il.code, line_type='TOP') if not line: line = InvoiceLine(agreement=self.agreement) line.update_top(product=il.product, quantity=il.quantity, price=il.price, pricedate=self.agreement.pricedate) self.final_lines.append(line) # Now do it again for trade lines. for il in incoming_lines: if not il.line_type == 'TRADE': continue line = self.reclaim_line(code=il.code, line_type='TRADE') if not line: line = InvoiceLine(agreement=self.agreement) line.update_trade(product=il.product, quantity=il.quantity, price=il.price, pricedate=self.agreement.pricedate) self.final_lines.append(line) # Save any lines in final_lines because they'll need pks for their children. for line in self.final_lines: line.save() print "Final lines BEFORE CHILD SYNC:" for line in self.final_lines: print "{}, {}, {}".format(line.code, line.quantity, line.traded) # loop through, adding mandatory items and syncing children until no changes are made. loops = 0 self.sync_all_children() while True: # Next, we need to process mandatory items. changed = self.add_mandatory_items() # Then, sync children again. changed = self.sync_all_children() or changed if not changed: break loops += 1 if loops >= 10: self.errors.append( "Probably an infinite loop in mandatories/children. Needs fixin." ) break # Next reclaim/create a permit line if needed. self.add_permit_lines() self.sanity_check() print "Final lines:" for line in self.final_lines: print "{}, {}, {}".format(line.code, line.quantity, line.traded) # Finally, any lines that are in existing_lines but not in final_lines should be deleted. for orphan in self.existing_lines: if orphan in self.final_lines: continue orphan.delete()
def update_invoice_lines(self): # The incoming invoice lines should look like: ''' [{ 'code': 'COPPER', 'quantity': 1 'traded': true // or not present. }, ...] ''' # coerce the incoming invoice lines: incoming_lines = [IL(line, updater=self) for line in self.blob['invoice_lines']] # If the IL constructor put any errors in, stop now. if self.errors: return # Next get every existing invoice line. self.existing_lines = list(self.agreement.invoice_lines.all()) self.unclaimed_lines = list(self.existing_lines) # Unclaim all existing lines. We may reclaim them soon. # this is the list of lines that will be on the agreement at the end: self.final_lines = [] for il in list(incoming_lines): if not il.price: incoming_lines.remove(il) self.messages.append('%s removed from the agreement because it is not available.' % il.code) continue available = getattr(il.price, 'available_mask', None) if available is None: available = il.price.available if not available: incoming_lines.remove(il) self.messages.append('%s removed from the agreement because it is no longer available.' % il.code) continue # First, do every line that came in from the system NOT traded. # Invoice Lines for these should all be TOP. # (This includes package, monitoring, alacarte, but not children, mandatory services...) for il in incoming_lines: if not il.line_type == 'TOP': continue # il is a fakey line. line = self.reclaim_line(code=il.code, line_type='TOP') if not line: line = InvoiceLine(agreement=self.agreement) line.update_top(product=il.product, quantity=il.quantity, price=il.price, pricedate=self.agreement.pricedate) self.final_lines.append(line) # Now do it again for trade lines. for il in incoming_lines: if not il.line_type == 'TRADE': continue line = self.reclaim_line(code=il.code, line_type='TRADE') if not line: line = InvoiceLine(agreement=self.agreement) line.update_trade(product=il.product, quantity=il.quantity, price=il.price, pricedate=self.agreement.pricedate) self.final_lines.append(line) # Save any lines in final_lines because they'll need pks for their children. for line in self.final_lines: line.save() print "Final lines BEFORE CHILD SYNC:" for line in self.final_lines: print "{}, {}, {}".format(line.code, line.quantity, line.traded) # loop through, adding mandatory items and syncing children until no changes are made. loops = 0 self.sync_all_children() while True: # Next, we need to process mandatory items. changed = self.add_mandatory_items() # Then, sync children again. changed = self.sync_all_children() or changed if not changed: break loops += 1 if loops >= 10: self.errors.append("Probably an infinite loop in mandatories/children. Needs fixin.") break # Next reclaim/create a permit line if needed. self.add_permit_lines() self.sanity_check() print "Final lines:" for line in self.final_lines: print "{}, {}, {}".format(line.code, line.quantity, line.traded) # Finally, any lines that are in existing_lines but not in final_lines should be deleted. for orphan in self.existing_lines: if orphan in self.final_lines: continue orphan.delete()