def report_stock(test_class, hsa, product_string, managers=None, products_back=""): """ Reports stock. """ stock_report = ProductReportsHelper(SupplyPoint(), Reports.SOH) stock_report.parse(product_string) product_list = " ".join(stock_report.reported_products()).strip() manager_msgs = [] if managers: for manager in managers: "%(hsa)s needs the following products: %(products)s. Respond 'ready %(hsa_id)s' when products are ready for pick up." manager_msgs.append(""" %(phone)s < %(confirm)s """ % {"phone": manager.default_connection.identity, "confirm": config.Messages.SUPERVISOR_SOH_NOTIFICATION % \ {"hsa": hsa.name, "products": products_back, "hsa_id": hsa.supply_point.code}}) a = """ %(phone)s > soh %(products)s %(phone)s < %(confirm)s %(manager_msgs)s """ % {"phone": hsa.default_connection.identity, "products": product_string, "confirm": config.Messages.SOH_ORDER_CONFIRM % {"products": product_list}, "manager_msgs": "".join(manager_msgs)} test_class.runScript(a)
def create_stock_report(report_type, supply_point, text, logger_msg=None, timestamp=None): """ Gets a stock report helper object parses it, and saves it. """ stock_report = ProductReportsHelper(supply_point, report_type, logger_msg, timestamp) stock_report.newparse(text) stock_report.save() return stock_report
def input_stock(request, facility_code, context={}, template="logistics/input_stock.html"): # TODO: replace this with something that depends on the current user # QUESTION: is it possible to make a dynamic form? errors = '' rms = get_object_or_404(SupplyPoint, code=facility_code) productstocks = [ p for p in rms.stocked_productstocks().order_by('product__name') ] if request.method == "POST": # we need to use the helper/aggregator so that when we update # the supervisor on resolved stockouts we can do it all in a # single message prh = ProductReportsHelper(rms, Reports.SOH) for stock in productstocks: try: if stock.product.sms_code in request.POST: quantity = request.POST[stock.product.sms_code].strip() if quantity: if not quantity.isdigit(): errors = ", ".join([errors, stock.product.name]) continue prh.add_product_stock(stock.product.sms_code, quantity, save=False) if "%s_receipt" % stock.product.sms_code in request.POST: receipt = request.POST["%s_receipt" % stock.product.sms_code].strip() if not receipt.isdigit(): errors = ", ".join([errors, stock.product.name]) continue if int(receipt) != 0: prh.add_product_receipt(stock.product.sms_code, receipt, save=False) if "%s_consumption" % stock.product.sms_code in request.POST: consumption = request.POST["%s_consumption" % stock.product.sms_code].strip() if consumption: if not consumption.isdigit(): errors = ", ".join([errors, stock.product.name]) continue prh.add_product_consumption(stock.product, consumption) if "%s_use_auto_consumption" % stock.product.sms_code in request.POST: rms.activate_auto_consumption(stock.product) else: rms.deactivate_auto_consumption(stock.product) except ValueError, e: errors = errors + unicode(e) if not errors: prh.save() return HttpResponseRedirect( reverse(stockonhand_facility, args=(rms.code, ))) errors = "Please enter all stock on hand and consumption as integers, for example:'100'. " + \ "The following fields had problems: " + errors.strip(', ')
def handle(self, text): sp = self.msg.logistics_contact.supply_point result = text.split() product_code = result[0] amt = result[1] p = Product.objects.get(product_code=product_code) prh = ProductReportsHelper(sp, Reports.SOH) prh.add_product_stock(p.sms_code, amt) prh.save() self.respond(_(config.Messages.STOCK_INQUIRY_CONFIRM) % {"quantity": amt, "product_name": p.name})
def handle(self, text): dupes = Message.objects.filter( direction="I", connection=self.msg.connection, text__iexact=self.msg.raw_text).exclude(pk=self.msg.logger_msg.pk) if settings.LOGISTICS_IGNORE_DUPE_RECEIPTS_WITHIN: dupe_ignore_threshold = datetime.utcnow() - timedelta( seconds=settings.LOGISTICS_IGNORE_DUPE_RECEIPTS_WITHIN) ignore = dupes.filter(date__gte=dupe_ignore_threshold) if ignore.count() and ProductReport.objects.filter( message__in=dupes).count(): return True # at the end of your receipt message you can write: # 'from xxx' to indicate the source of the supplies. # this is used in the stock transfer workflow # TODO: less brittle parsing if "from" in text.lower().split(): splittext = text.lower().split() text = " ".join(splittext[:splittext.index("from")]) supplier = " ".join(splittext[splittext.index("from") + 1:]) else: supplier = None # parse the report and save as normal receipt stock_report = ProductReportsHelper( self.msg.logistics_contact.supply_point, Reports.REC, self.msg.logger_msg) stock_report.parse(text) # check max stock levels max_level_function = get_max_level_function() if max_level_function: try: max_level_function(stock_report) except TooMuchStockError, e: # bit of a hack, also check if there was a recent message # that matched this and if so force it through override_threshold = datetime.utcnow() - timedelta(seconds=60 * 60 * 4) override = dupes.filter(date__gte=override_threshold) if override.count() == 0: self.respond( config.Messages.TOO_MUCH_STOCK % { 'keyword': self.msg.text.split()[0], 'req': e.amount, 'prod': e.product, 'max': e.max, }) return True
def handle(self, message): """Add your main application logic in the handle phase.""" should_proceed, return_code = self._check_preconditions(message) if not should_proceed: return return_code try: message.text = self._clean_message(message.text) stock_report = ProductReportsHelper(message.logistics_contact.supply_point, Reports.SOH, message.logger_msg) stock_report.parse(message.text) stock_report.save() self._send_responses(message, stock_report) return True except ValueError, e: # a parsing error, with a user-friendly message message.respond(unicode(e)) return
def handle(self, text): # at the end of your receipt message you can write: # 'from xxx' to indicate the source of the supplies. # this is used in the stock transfer workflow # TODO: less brittle parsing if "from" in text.lower().split(): splittext = text.lower().split() text = " ".join(splittext[:splittext.index("from")]) supplier = " ".join(splittext[splittext.index("from") + 1:]) else: supplier = None # parse the report and save as normal receipt stock_report = ProductReportsHelper(self.msg.logistics_contact.supply_point, Reports.REC, self.msg.logger_msg) stock_report.parse(text) stock_report.save() # Close pending requests. This logic only applies if you are using the # StockRequest workflow, but should not break anything if you are not StockRequest.close_pending_from_receipt_report(stock_report, self.msg.logistics_contact) # fill in transfers, if there were any if supplier is not None: StockTransfer.create_from_receipt_report(stock_report, supplier) self.respond(_(config.Messages.RECEIPT_FROM_CONFIRM), products=" ".join(stock_report.reported_products()).strip(), supplier=supplier) else: self.respond(_(config.Messages.RECEIPT_CONFIRM), products=" ".join(stock_report.reported_products()).strip())
def handle(self, text): dupes = Message.objects.filter(direction="I", connection=self.msg.connection, text__iexact=self.msg.raw_text).exclude(pk=self.msg.logger_msg.pk) if settings.LOGISTICS_IGNORE_DUPE_RECEIPTS_WITHIN: dupe_ignore_threshold = datetime.utcnow() - timedelta(seconds=settings.LOGISTICS_IGNORE_DUPE_RECEIPTS_WITHIN) ignore = dupes.filter(date__gte=dupe_ignore_threshold) if ignore.count() and ProductReport.objects.filter(message__in=dupes).count(): return True # at the end of your receipt message you can write: # 'from xxx' to indicate the source of the supplies. # this is used in the stock transfer workflow # TODO: less brittle parsing if "from" in text.lower().split(): splittext = text.lower().split() text = " ".join(splittext[:splittext.index("from")]) supplier = " ".join(splittext[splittext.index("from") + 1:]) else: supplier = None # parse the report and save as normal receipt stock_report = ProductReportsHelper(self.msg.logistics_contact.supply_point, Reports.REC, self.msg.logger_msg) stock_report.parse(text) # check max stock levels max_level_function = get_max_level_function() if max_level_function: try: max_level_function(stock_report) except TooMuchStockError, e: # bit of a hack, also check if there was a recent message # that matched this and if so force it through override_threshold = datetime.utcnow() - timedelta(seconds=60*60*4) override = dupes.filter(date__gte=override_threshold) if override.count() == 0: self.respond(config.Messages.TOO_MUCH_STOCK % { 'keyword': self.msg.text.split()[0], 'req': e.amount, 'prod': e.product, 'max': e.max, }) return True
def testProductReportsHelper(self): sdp = SupplyPoint() m = Message() p = ProductReportsHelper(sdp, Reports.SOH, m) p.add_product_stock('lf', 10, save=False) p.add_product_stock('mc', 30, save=False) p.add_product_stock('aa', 0, save=False) p.add_product_stock('oq', 0, save=False) self.assertEquals(p.all(), "lf 10, aa 0, oq 0, mc 30") self.assertEquals(p.stockouts(), "aa oq") my_iter = p._getTokens("ab10cd20") self.assertEquals(my_iter.next(), 'ab') self.assertEquals(my_iter.next(), '10') self.assertEquals(my_iter.next(), 'cd') self.assertEquals(my_iter.next(), '20')
def input_stock(request, facility_code, context={}, template="logistics/input_stock.html"): # TODO: replace this with something that depends on the current user # QUESTION: is it possible to make a dynamic form? errors = '' rms = get_object_or_404(SupplyPoint, code=facility_code) productstocks = [p for p in ProductStock.objects.filter(supply_point=rms).order_by('product__name')] if request.method == "POST": # we need to use the helper/aggregator so that when we update # the supervisor on resolved stockouts we can do it all in a # single message prh = ProductReportsHelper(rms, Reports.SOH) for stock in productstocks: try: if stock.product.sms_code in request.POST: quantity = request.POST[stock.product.sms_code].strip() if quantity: if not quantity.isdigit(): errors = ", ".join([errors, stock.product.name]) continue prh.add_product_stock(stock.product.sms_code, quantity, save=False) if "%s_receipt" % stock.product.sms_code in request.POST: receipt = request.POST["%s_receipt" % stock.product.sms_code].strip() if not receipt.isdigit(): errors = ", ".join([errors, stock.product.name]) continue if int(receipt) != 0: prh.add_product_receipt(stock.product.sms_code, receipt, save=False) if "%s_consumption" % stock.product.sms_code in request.POST: consumption = request.POST["%s_consumption" % stock.product.sms_code].strip() if consumption: if not consumption.isdigit(): errors = ", ".join([errors, stock.product.name]) continue prh.add_product_consumption(stock.product, consumption) if "%s_use_auto_consumption" % stock.product.sms_code in request.POST: rms.activate_auto_consumption(stock.product) else: rms.deactivate_auto_consumption(stock.product) except ValueError, e: errors = errors + unicode(e) if not errors: prh.save() return HttpResponseRedirect(reverse(stockonhand_facility, args=(rms.code,))) errors = "Please enter all stock on hand and consumption as integers, for example:'100'. " + \ "The following fields had problems: " + errors.strip(', ')
def handle(self, text): pending = StockTransfer.pending_transfers().filter\ (receiver=self.msg.logistics_contact.supply_point).all() if len(pending) == 0: self.respond(config.Messages.NO_PENDING_TRANSFERS) else: # the easiest way to mark these in the database, # is to make a fake stock report # of the pending transfers, considering them as receipts stock_report = ProductReportsHelper(self.msg.logistics_contact.supply_point, Reports.REC) stock_report.parse(" ".join([p.sms_format() for p in pending])) stock_report.save() # close the pending transfers now = datetime.utcnow() for p in pending: p.confirm(now) self.respond(config.Messages.CONFIRM_RESPONSE, receiver=self.msg.logistics_contact.name, products=", ".join([p.sms_format() for p in pending]))
def testProductReportsHelper(self): sdp = SupplyPoint() m = Message() p = ProductReportsHelper(sdp, Reports.SOH, m) p.add_product_stock('lf',10, save=False) p.add_product_stock('mc',30, save=False) p.add_product_stock('aa',0, save=False) p.add_product_stock('oq',0, save=False) self.assertEquals(p.all(), "lf 10, aa 0, oq 0, mc 30") self.assertEquals(p.stockouts(), "aa oq") my_iter = p._getTokens("ab10cd20") self.assertEquals(my_iter.next(),'ab') self.assertEquals(my_iter.next(),'10') self.assertEquals(my_iter.next(),'cd') self.assertEquals(my_iter.next(),'20')
def report_stock(test_class, hsa, product_string, managers=None, products_back=""): """ Reports stock. """ stock_report = ProductReportsHelper(SupplyPoint(), Reports.SOH) stock_report.parse(product_string) product_list = " ".join(stock_report.reported_products()).strip() manager_msgs = [] if managers: for manager in managers: "%(hsa)s needs the following products: %(products)s. Respond 'ready %(hsa_id)s' when products are ready for pick up." manager_msgs.append(""" %(phone)s < %(confirm)s """ % {"phone": manager.default_connection.identity, "confirm": config.Messages.SUPERVISOR_SOH_NOTIFICATION % \ {"hsa": hsa.name, "products": products_back, "hsa_id": hsa.supply_point.code}}) a = """ %(phone)s > soh %(products)s %(phone)s < %(confirm)s %(manager_msgs)s """ % { "phone": hsa.default_connection.identity, "products": product_string, "confirm": config.Messages.SOH_ORDER_CONFIRM % { "products": product_list }, "manager_msgs": "".join(manager_msgs) } test_class.runScript(a)
def handle(self, message): should_proceed, return_code = self._check_preconditions(message) if not should_proceed: return return_code try: message.text = self._clean_message(message.text) stock_report = ProductReportsHelper( message.logistics_contact.supply_point, Reports.SOH, message.logger_msg, validator=SohAndReceiptValidator()) stock_report.parse(message.text) stock_report.validate() stock_report.save() self._send_responses(message, stock_report) return True except ValueError, e: # a parsing error, with a user-friendly message message.respond(unicode(e)) return
def create_stock_report(report_type, supply_point, text, logger_msg=None, timestamp=None, additional_validation=None): """ Gets a stock report helper object parses it, and saves it. """ additional_validation = additional_validation or (lambda report: True) stock_report = ProductReportsHelper(supply_point, report_type, logger_msg, timestamp) stock_report.newparse(text) additional_validation(stock_report) stock_report.save() return stock_report
def handle(self, text): sp = self.msg.logistics_contact.supply_point result = text.split() product_code = result[0] amt = result[1] p = Product.objects.get(product_code=product_code) prh = ProductReportsHelper(sp, Reports.SOH) prh.add_product_stock(p.sms_code, amt) prh.save() self.respond( _(config.Messages.STOCK_INQUIRY_CONFIRM) % { "quantity": amt, "product_name": p.name })
def handle (self, message): should_proceed, return_code = self._check_preconditions(message) if not should_proceed: return return_code try: message.text = self._clean_message(message.text) stock_report = ProductReportsHelper(message.logistics_contact.supply_point, Reports.SOH, message.logger_msg, validator=SohAndReceiptValidator()) stock_report.parse(message.text) stock_report.validate() stock_report.save() self._send_responses(message, stock_report) return True except ValueError, e: # a parsing error, with a user-friendly message message.respond(unicode(e)) return
def handle(self, message): """Add your main application logic in the handle phase.""" should_proceed, return_code = self._check_preconditions(message) if not should_proceed: return return_code try: message.text = self._clean_message(message.text) stock_report = ProductReportsHelper( message.logistics_contact.supply_point, Reports.SOH, message.logger_msg) stock_report.parse(message.text) stock_report.save() self._send_responses(message, stock_report) return True except ValueError, e: # a parsing error, with a user-friendly message message.respond(unicode(e)) return
def handle(self, text): pending = StockTransfer.pending_transfers().filter\ (receiver=self.msg.logistics_contact.supply_point).all() if len(pending) == 0: self.respond(config.Messages.NO_PENDING_TRANSFERS) else: # the easiest way to mark these in the database, # is to make a fake stock report # of the pending transfers, considering them as receipts stock_report = ProductReportsHelper( self.msg.logistics_contact.supply_point, Reports.REC) stock_report.parse(" ".join([p.sms_format() for p in pending])) stock_report.save() # close the pending transfers now = datetime.utcnow() for p in pending: p.confirm(now) self.respond(config.Messages.CONFIRM_RESPONSE, receiver=self.msg.logistics_contact.name, products=", ".join([p.sms_format() for p in pending]))
def process_xform(submission): from logistics.models import ProductReportsHelper from logistics.util import config from logistics import const try: health_provider = submission.connection.contact.healthproviderbase.healthprovider except: logging.error('%s contact is not a health provider' % submission.connection.identity) return if not submission.message: logging.error('%s sent an empty message' % submission.connection.identity) return if health_provider.facility is None: logging.error('%s has no associated facility' % submission.connection.identity) return values_count = len(submission.submission_values()) try: if submission.xform.keyword == stock_reports[0]: stock_report = ProductReportsHelper(health_provider.facility.supply_point, const.Reports.SOH, submission.message) if values_count > 0: stock_report.add_product_receipt(config.Products.SIX_PACK, submission.eav.act_spd) if values_count > 1: stock_report.add_product_stock(config.Products.SIX_PACK, submission.eav.act_sps) if values_count > 2: stock_report.add_product_receipt(config.Products.TWELVE_PACK, submission.eav.act_tpd) if values_count > 3: stock_report.add_product_stock(config.Products.TWELVE_PACK, submission.eav.act_tps) if values_count > 4: stock_report.add_product_receipt(config.Products.EIGHTEEN_PACK, submission.eav.act_epd) if values_count > 5: stock_report.add_product_stock(config.Products.EIGHTEEN_PACK, submission.eav.act_eps) if values_count > 6: stock_report.add_product_receipt(config.Products.TWENTY_FOUR_PACK, submission.eav.act_fpd) if values_count > 7: stock_report.add_product_stock(config.Products.TWENTY_FOUR_PACK, submission.eav.act_fps) stock_report.save() elif submission.xform.keyword == stock_reports[1]: stock_report = ProductReportsHelper(health_provider.facility.supply_point, const.Reports.SOH, submission.message) if values_count > 0: stock_report.add_product_receipt(config.Products.OTHER_ACT_STOCK, submission.eav.qun_oad) if values_count > 1: stock_report.add_product_stock(config.Products.OTHER_ACT_STOCK, submission.eav.qun_oas) if values_count > 2: stock_report.add_product_receipt(config.Products.QUININE, submission.eav.qun_qud) if values_count > 3: stock_report.add_product_stock(config.Products.QUININE, submission.eav.qun_qus) stock_report.save() elif submission.xform.keyword == stock_reports[2]: stock_report = ProductReportsHelper(health_provider.facility.supply_point, const.Reports.SOH, submission.message) if values_count > 0: stock_report.add_product_receipt(config.Products.RAPID_DIAGNOSTIC_TEST, submission.eav.rdt_rdd) if values_count > 1: stock_report.add_product_stock(config.Products.RAPID_DIAGNOSTIC_TEST, submission.eav.rdt_rds) stock_report.save() except Exception, e: if submission.xform.keyword in stock_reports: submission.response = unicode(e) submission.has_errors = True submission.save() return