Beispiel #1
0
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
Beispiel #3
0
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(', ')
Beispiel #4
0
 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
Beispiel #6
0
 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
Beispiel #7
0
 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())
Beispiel #8
0
    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
Beispiel #9
0
 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(', ')
Beispiel #11
0
 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]))
         
Beispiel #12
0
 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')
Beispiel #13
0
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)
Beispiel #14
0
 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
Beispiel #16
0
 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
         })
Beispiel #17
0
 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
Beispiel #18
0
 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
Beispiel #19
0
    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]))
Beispiel #20
0
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