def get_manifests(links):
    log.info("Getting manifests from links: %s", links)
    settings = config_get_group('canada_post_dp_shipping')
    cpa_kwargs = canada_post_api_kwargs(settings)
    cpa = CanadaPostAPI(**cpa_kwargs)
    manifests = []
    for link in links:
        log.debug("Getting manifest from %s", link['href'])
        try:
            cpa_manifest = time_f(cpa.get_manifest,
                                  'canada-post-dp-shipping.get-manifest', link)
            manifest = Manifest(manifest=cpa_manifest)
            manifest_pdf = time_f(cpa.get_artifact,
                                  'canada-post-dp-shipping.get-artifact',
                                  cpa_manifest)
            filename = os.path.basename(link['href'].rstrip('/'))
            if not filename.endswith('.pdf'):
                filename += '.pdf'
            manifest.artifact = File(manifest_pdf, filename)
            manifest.save()
            shipments = time_f(cpa.get_manifest_shipments,
                               'canada-post-dp-shipping.get-manifest-shipments',
                               cpa_manifest)
            for shipment_id in shipments:
                log.info("Setting manifest for shipment %s", shipment_id)
                try:
                    shipment = Shipment.objects.select_related().get(id=shipment_id)
                    shipping_detail = shipment.parcel.shipping_detail
                    shipping_detail.manifest = manifest
                    shipping_detail.save()
                except Shipment.DoesNotExist:
                    log.error("Requested shipment does not exist")
            manifests.append(manifest)
        except Exception, e:
            log.error("Error processing manifest: %s", e, exc_info=True)
Example #2
0
def transmit_shipments(queryset=None, send_msg=None):
    log.info("transmit_shipments invoked")
    log.debug("queryset: %s", str(queryset))
    if send_msg is None:
        send_msg = lambda x: x

    if queryset is None:
        queryset = OrderShippingService.objects.all()

    from satchmo_store.shop.models import Config
    shop_details = Config.objects.get_current()
    settings = config_get_group('canada_post_dp_shipping')
    cpa_kwargs = canada_post_api_kwargs(settings)
    cpa = CanadaPostAPI(**cpa_kwargs)
    origin = get_origin(shop_details)

    groups = []
    order_shippings = []

    for order_shipping in queryset.filter(transmitted=False):
        log.debug("processing order shipping: %s", order_shipping)
        if order_shipping.shipments_created():
            log.debug("shipments created")
            group = unicode(order_shipping.shipping_group())
            groups.append(group)
            order_shippings.append(order_shipping)
        else:
            log.debug("shipments not created")
    log.debug("using groups: %s", groups)
    if groups:
        log.info("transmitting shipments")
        links = time_f(cpa.transmit_shipments,
                       'canada-post-dp-shipping.transmit-shipments', origin,
                       groups)
        log.debug("received manifests: %s", links)
        log.debug("marking order shippings as transmitted")
        for order_shipping in order_shippings:
            order_shipping.transmitted = True
            order_shipping.save()
        manifest_count = len(links)
        log.info("received %d manifests", manifest_count)
        send_msg(
            ungettext_lazy(
                "{count} manifest generated. It will be sent via email in a "
                "couple of minutes".format(count=manifest_count),
                "{count} manifests generated. They will be sent via email in a "
                "couple of minutes".format(count=manifest_count),
                manifest_count))
        if USE_CELERY:
            get_manifests_async.apply_async(args=(links, ), cowntdown=1)
        else:
            get_manifests(links)

    group_count = len(groups)
    send_msg(
        ungettext_lazy(
            "Transmitted shipments for {count} group".format(
                count=group_count),
            "Transmitted shipments for {count} groups".format(
                count=group_count), group_count))
def transmit_shipments(queryset=None, send_msg=None):
    log.info("transmit_shipments invoked")
    log.debug("queryset: %s", str(queryset))
    if send_msg is None:
        send_msg = lambda x: x

    if queryset is None:
        queryset = OrderShippingService.objects.all()

    from satchmo_store.shop.models import Config
    shop_details = Config.objects.get_current()
    settings = config_get_group('canada_post_dp_shipping')
    cpa_kwargs = canada_post_api_kwargs(settings)
    cpa = CanadaPostAPI(**cpa_kwargs)
    origin = get_origin(shop_details)

    groups = []
    order_shippings = []

    for order_shipping in queryset.filter(
            transmitted=False):
        log.debug("processing order shipping: %s", order_shipping)
        if order_shipping.shipments_created():
            log.debug("shipments created")
            group = unicode(order_shipping.shipping_group())
            groups.append(group)
            order_shippings.append(order_shipping)
        else:
            log.debug("shipments not created")
    log.debug("using groups: %s", groups)
    if groups:
        log.info("transmitting shipments")
        links = time_f(cpa.transmit_shipments,
                       'canada-post-dp-shipping.transmit-shipments',
                       origin, groups)
        log.debug("received manifests: %s", links)
        log.debug("marking order shippings as transmitted")
        for order_shipping in order_shippings:
            order_shipping.transmitted = True
            order_shipping.save()
        manifest_count = len(links)
        log.info("received %d manifests", manifest_count)
        send_msg(ungettext_lazy(
            "{count} manifest generated. It will be sent via email in a "
            "couple of minutes".format(count=manifest_count),
            "{count} manifests generated. They will be sent via email in a "
            "couple of minutes".format(count=manifest_count), manifest_count))
        if USE_CELERY:
            get_manifests_async.apply_async(args=(links,), cowntdown=1)
        else:
            get_manifests(links)

    group_count = len(groups)
    send_msg(ungettext_lazy(
        "Transmitted shipments for {count} group".format(count=group_count),
        "Transmitted shipments for {count} groups".format(count=group_count),
        group_count))
    def get_rates(self, cart, contact):
        from satchmo_store.shop.models import Config
        error_ret = False, None, []
        shop_details = Config.objects.get_current()

        # always use production api keys for get_rates, you don't get charged
        #  anyways
        cpa_kwargs = canada_post_api_kwargs(self.settings, production=True)

        cpa = CanadaPostAPI(**cpa_kwargs)

        # parcels is a list of (Parcel, pack(dimensions))
        parcels, rest = self.make_parcels(cart)
        if rest:
            from django.contrib.sites.models import Site
            site = Site.objects.get_current()
            error_message = (u"There's not boxes big enough for some of these "
                             u"products: ({})").format(u", ".join(
                u"Package({})".format(unicode(p)) for p in rest))
            subject = u"There's not boxes big enough for some products"
            send_store_mail(subject, context={ 'site': site,
                                               'product_list': rest },
                            template=("canada_post_dp_shipping/admin/mail/"
                                      "add_boxes.txt"), send_to_store=True)
            raise ParcelDimensionError, error_message
        log.debug(u"Calculated Parcels: [%s]", u",".join(u"({},[{}])".format(
            pr, u",".join(unicode(pk) for pk in pks)) for pr, pks in parcels))
        origin = get_origin(shop_details)
        destination = get_destination(contact)

        services = []
        for parcel, packs in parcels:
            # rates depend on dimensions + origin + destination only
            cache_key = "CP-GetRates-{W}-{l}x{w}x{h}-{fr}-{to}".format(
                W=parcel.weight, w=parcel.width, h=parcel.height, l=parcel.length,
                fr=origin.postal_code, to=destination.postal_code
            )
            parcel_services = cache.get(cache_key)
            if parcel_services is None:
                try:
                    parcel_services = time_f(
                        cpa.get_rates, 'canada-post-dp-shipping.get-rates',
                        parcel, origin, destination)
                except CanadaPostError, e:
                    if self.settings.RAISE_TOO_LARGE.value and e.code == 9111:
                        raise ParcelDimensionError, e.message
                    else:
                        log.error(u"Canada Post returned with error: %s|%s",
                                  e.code, e.message)
                    parcel_services = []
                cache.set(cache_key, parcel_services)

            # so services is [(Service, parcel, [packs]),...]
            services.extend(product(filter(lambda s: s.code == self.service_code,
                                      parcel_services), [parcel], [packs]))
Example #5
0
    def create_shipments(self, request, id):
        from satchmo_store.shop.models import Config
        order_shipping = get_object_or_404(OrderShippingService, id=id)

        if request.method == 'GET':
            opts = self.model._meta
            title = _("Please confirm the parcels size and weight")
            object_name = unicode(opts.verbose_name)
            app_label = opts.app_label
            context = {
                "title": title,
                "object_name": object_name,
                "object": order_shipping,
                "opts": opts,
                "root_path": self.admin_site.root_path,
                "app_label": app_label,
                }
            return render(request,
                          ("canada_post_dp_shipping/admin/"
                           "confirm_shipments.html"),
                          context)
        elif request.REQUEST.get('post', None) == "yes":
            # else method is POST
            shop_details = Config.objects.get_current()
            cpa_kwargs = canada_post_api_kwargs(self.settings)
            cpa = CanadaPostAPI(**cpa_kwargs)
            origin = get_origin(shop_details)

            destination = get_destination(order_shipping.order.contact)
            group = unicode(order_shipping.shipping_group())
            cnt = 0
            exs = 0
            for parcel in (order_shipping.parceldescription_set
                           .select_related().all()):
                try:
                    if parcel.shipment:
                        exs += 1
                except Shipment.DoesNotExist:
                    shipment = cpa.create_shipment(
                        parcel=parcel.get_parcel(), origin=origin,
                        destination=destination,
                        service=order_shipping.get_service(), group=group)
                    Shipment(shipment=shipment, parcel=parcel).save()
                    cnt += 1
            self.message_user(request, _("{count} shipments created for order "
                                         "{order}").format(
                count=cnt, order=order_shipping.order))
            if exs > 0:
                messages.warning(request, _("{count} shipments already existed "
                                             "for {order}").format(
                    count=exs, order=order_shipping.order))
        else:
            messages.error(request, _("Unexpected error, please retry"))
        return HttpResponseRedirect("..")
Example #6
0
    def void_shipments(self, request, queryset=None, id=-1):
        log.info("void shipments %s", queryset or str(id))
        if queryset is None:
            if queryset is None:
                queryset = [get_object_or_404(OrderShippingService,
                                              id=id)]
            else:
                queryset = queryset.select_related()

        cpa_kwargs = canada_post_api_kwargs(self.settings)
        cpa = CanadaPostAPI(**cpa_kwargs)
        errcnt = 0
        gdcnt = 0
        dne = 0
        for detail in queryset:
            log.debug('Processing detail: %s', detail)
            for parcel in detail.parceldescription_set.all().select_related():
                log.debug("Processing parcel: %s", parcel)
                try:
                    shipment = parcel.shipment
                    log.debug("Got shipment %s", shipment)
                    cpa_shipment = shipment.get_shipment()
                    if not time_f(cpa.void_shipment,
                                  'canada-post-dp-shipping.void-shipment',
                                  cpa_shipment):
                        log.warn("Problem voiding shipment: %s", cpa_shipment)
                        errcnt += 1
                        self.message_user(request, _("Could not void shipment "
                                                     "{shipment_id} for order "
                                                     "{order_id}").format(
                            shipment_id=shipment.id, order_id=detail.order.id))
                    else:
                        log.debug("Shipment voided, deleting from DB")
                        gdcnt += 1
                        shipment.delete()
                except Shipment.DoesNotExist:
                    log.debug("Shipment does not exist!")
                    dne += 1

        if not errcnt:
            log.info("All shipments voided")
            self.message_user(request, _("All shipments voided"))
        else:
            log.warn("%d shipments voided, %d problems", gdcnt, errcnt)
            messages.warning(request, _("{good_count} shipments voided, "
                                        "{bad_count} problems").format(
                good_count=gdcnt, bad_count=errcnt))
        if dne:
            log.debug("%d shipments didn't exist", dne)
            self.message_user(request, _("{count} shipments didn't "
                                         "exist").format(count=dne))
        if id >= 0:
            return HttpResponseRedirect("..")
Example #7
0
    def get_rates(self, cart, contact):
        from satchmo_store.shop.models import Config
        error_ret = False, None, None
        shop_details = Config.objects.get_current()

        # always use production api keys for get_rates, you don't get charged
        #  anyways
        cpa_kwargs = canada_post_api_kwargs(self.settings, production=True)

        cpa = CanadaPostAPI(**cpa_kwargs)

        # parcels is a list of (Parcel, pack(dimensions))
        parcels, rest = self.make_parcels(cart)
        if rest:
            log.error("There's not boxes big enough for some of these "
                      "products: {}".format(rest))
            return error_ret
        log.debug("Calculated Parcels: [%s]", ",".join("({})".format(unicode(p))
                                                       for p in parcels))
        origin = get_origin(shop_details)
        destination = get_destination(contact)

        services = []
        for parcel, packs in parcels:
            # rates depend on dimensions + origin + destination only
            cache_key = "CP-GetRates-{W}-{l}x{w}x{h}-{fr}-{to}".format(
                W=parcel.weight, w=parcel.width, h=parcel.height, l=parcel.length,
                fr=origin.postal_code, to=destination.postal_code
            )
            if cache.has_key(cache_key):
                parcel_services = cache.get(cache_key)
            else:
                try:
                    parcel_services = cpa.get_rates(parcel, origin, destination)
                except CanadaPostError, e:
                    if self.settings.RAISE_TOO_LARGE.value and e.code == 9111:
                        raise ParcelTooLarge, e.message
                    parcel_services = []
                cache.set(cache_key, parcel_services)

            # so services is [(Service, parcel, [packs]),...]
            services.extend(product(filter(lambda s: s.code == self.service_code,
                                      parcel_services), [parcel], [packs]))
Example #8
0
    def void_shipments(self, request, queryset=None, id=-1):
        if queryset is None:
            if queryset is None:
                queryset = [get_object_or_404(OrderShippingService,
                                              id=id)]
            else:
                queryset = queryset.select_related()

        cpa_kwargs = canada_post_api_kwargs(self.settings)
        cpa = CanadaPostAPI(**cpa_kwargs)
        errcnt = 0
        gdcnt = 0
        dne = 0
        for detail in queryset:
            for parcel in detail.parceldescription_set.all().select_related():
                try:
                    shipment = parcel.shipment
                    cpa_shipment = shipment.get_shipment()
                    if not cpa.void_shipment(cpa_shipment):
                        errcnt += 1
                        self.message_user(request, _("Could not void shipment "
                                                     "{shipment_id} for order "
                                                     "{order_id}").format(
                            shipment_id=shipment.id, order_id=detail.order.id))
                    else:
                        gdcnt += 1
                        shipment.delete()
                except Shipment.DoesNotExist:
                    dne += 1

        if not errcnt:
            self.message_user(request, _("All shipments voided"))
        else:
            messages.warning(request, _("{good_count} shipments voided, "
                                        "{bad_count} problems").format(
                good_count=gdcnt, bad_count=errcnt))
        if dne:
            self.message_user(request, _("{count} shipments didn't "
                                         "exist").format(count=dne))
        if id >= 0:
            return HttpResponseRedirect("..")
Example #9
0
    def get_labels(self, request, queryset=None, id=-1):
        if queryset is None:
            queryset = [get_object_or_404(OrderShippingService,
                                          id=id)]
        else:
            queryset = queryset.select_related()

        args = canada_post_api_kwargs(self.settings)

        files = []
        orders = []
        for detail in queryset:
            for parcel in detail.parceldescription_set.select_related().all():
                shipment = parcel.shipment
                if not shipment.label:
                    try:
                        shipment.download_label(args['username'],
                                                args['password'])
                    except Shipment.Wait:
                        self.message_user(_("Failed downloading label for "
                                            "shipment {id} because the "
                                            "Canada Post server is busy, "
                                            "please wait a couple of minutes "
                                            "and try again").format(
                            id=shipment.id))
                files.append(shipment.label.file)
            orders.append(detail.order)

        tmp = tempfile.mkstemp(suffix=".zip")
        tf = zipfile.ZipFile(tmp[1], mode="w")
        for fileobj in files:
            filename = os.path.basename(fileobj.name)
            tf.write(fileobj.name, filename)
        tf.close()

        response = HttpResponse(File(file(tmp[1])), mimetype="application/zip")
        response['Content-disposition'] = ('attachment; '
                                           'filename='
                                           '"labels_for_orders_{}.zip"').format(
            "-".join(str(o.id) for o in orders))
        return response
Example #10
0
def get_manifests(links):
    log.info("Getting manifests from links: %s", links)
    settings = config_get_group('canada_post_dp_shipping')
    cpa_kwargs = canada_post_api_kwargs(settings)
    cpa = CanadaPostAPI(**cpa_kwargs)
    manifests = []
    for link in links:
        log.debug("Getting manifest from %s", link['href'])
        try:
            cpa_manifest = time_f(cpa.get_manifest,
                                  'canada-post-dp-shipping.get-manifest', link)
            manifest = Manifest(manifest=cpa_manifest)
            manifest_pdf = time_f(cpa.get_artifact,
                                  'canada-post-dp-shipping.get-artifact',
                                  cpa_manifest)
            filename = os.path.basename(link['href'].rstrip('/'))
            if not filename.endswith('.pdf'):
                filename += '.pdf'
            manifest.artifact = File(manifest_pdf, filename)
            manifest.save()
            shipments = time_f(
                cpa.get_manifest_shipments,
                'canada-post-dp-shipping.get-manifest-shipments', cpa_manifest)
            for shipment_id in shipments:
                log.info("Setting manifest for shipment %s", shipment_id)
                try:
                    shipment = Shipment.objects.select_related().get(
                        id=shipment_id)
                    shipping_detail = shipment.parcel.shipping_detail
                    shipping_detail.manifest = manifest
                    shipping_detail.save()
                except Shipment.DoesNotExist:
                    log.error("Requested shipment does not exist")
            manifests.append(manifest)
        except Exception, e:
            log.error("Error processing manifest: %s", e, exc_info=True)
Example #11
0
    def create_shipments(self, request, id):
        log.info("Creating shipments for %s", str(id))
        from satchmo_store.shop.models import Config
        order_shipping = get_object_or_404(OrderShippingService, id=id)
        log.debug("Got OrderShippingService: %s", order_shipping)

        if request.method == 'GET':
            log.debug("GET. Ask for confirmation")
            opts = self.model._meta
            title = _("Please confirm the parcels size and weight")
            object_name = unicode(opts.verbose_name)
            app_label = opts.app_label
            context = {
                "title": title,
                "object_name": object_name,
                "object": order_shipping,
                "opts": opts,
                "root_path": self.admin_site.root_path,
                "app_label": app_label,
                }
            return render(request,
                          ("canada_post_dp_shipping/admin/"
                           "confirm_shipments.html"),
                          context)
        elif request.REQUEST.get('post', None) == "yes":
            log.debug("POST with value=yes")
            # else method is POST
            shop_details = Config.objects.get_current()
            cpa_kwargs = canada_post_api_kwargs(self.settings)
            cpa = CanadaPostAPI(**cpa_kwargs)
            origin = get_origin(shop_details)

            destination = get_destination(order_shipping.order.contact)
            options = None
            if destination.country_code != 'CA':
                # TODO: make this selectable through website
                options = [Option(code='RASE')]
            group = unicode(order_shipping.shipping_group())
            cnt = 0
            exs = 0
            for parcel in (order_shipping.parceldescription_set
                           .select_related().all()):
                log.debug("Creating shipment for parcel %s", parcel)
                try:
                    if parcel.shipment:
                        log.warn("Shipment already existed in DB! %s",
                                 parcel.shipment)
                        exs += 1
                except Shipment.DoesNotExist:
                    log.debug("Creating shipment")
                    cpa_ship = time_f(
                        cpa.create_shipment,
                        'canada-post-dp-shipping.create-shipping',
                        parcel=parcel.get_parcel(), origin=origin,
                        destination=destination,
                        service=order_shipping.get_service(), group=group,
                        options=options)
                    shipment = Shipment(shipment=cpa_ship, parcel=parcel)
                    shipment.save()
                    log.debug("Shipment created: %s", shipment)
                    if USE_CELERY:
                        from canada_post_dp_shipping.tasks import get_label
                        log.debug("Calling get_label celery task")
                        get_label.apply_async(args=(shipment.id,
                                                    cpa.auth.username,
                                                    cpa.auth.password),
                                              # download labels in 3 minutes
                                              countdown=3*60)

                cnt += 1
            log.info("%d shipments created for order %s",
                     cnt, order_shipping.order)
            self.message_user(request, _(u"{count} shipments created for order "
                                         u"{order}").format(
                count=cnt, order=order_shipping.order))
            if USE_CELERY:
                log.debug("Using celery. Task to download labels should run in "
                          "3 minutes")
                self.message_user(request, _(u"Shipping labels will be "
                                             u"automatically downloaded in "
                                             u"three minutes"))
            if exs > 0:
                log.warn("%d shipments already existed for order %s",
                         exs, order_shipping.order)
                messages.warning(request, _(u"{count} shipments already existed "
                                            u"for {order}").format(
                    count=exs, order=order_shipping.order))
        else:
            messages.error(request, _("Unexpected error, please retry"))
        return HttpResponseRedirect("..")
Example #12
0
    def get_labels(self, request, queryset=None, id=-1):
        log.info("get_labels for %s", queryset or str(id))
        if queryset is None:
            queryset = [get_object_or_404(OrderShippingService,
                                          id=id)]
        else:
            queryset = queryset.select_related()

        args = canada_post_api_kwargs(self.settings)

        files = []
        orders = []
        for shipping_service in queryset:
            log.debug("processing shipping service %s", shipping_service)
            try:
                for parcel in (shipping_service.parceldescription_set
                               .select_related().all()):
                    log.debug("processing parcel %s", parcel)
                    shipment = parcel.shipment
                    log.debug("shipment: %s", shipment)
                    if not shipment.label:
                        log.debug("No label, downloading")
                        try:
                            shipment.download_label(args['username'],
                                                    args['password'])
                        except Shipment.Wait:
                            log.debug("Failed downloading label. "
                                      "Ask user to retry")
                            self.message_user(_("Failed downloading label for "
                                                "shipment {id} because the "
                                                "Canada Post server is busy, "
                                                "please wait a couple of "
                                                "minutes and try again").format(
                                id=shipment.id))
                    files.append(shipment.label.file)
                    log.debug("Got file (%d)", len(files))
            except Shipment.DoesNotExist:
                log.debug("Requested labels, but shipment had not been created "
                          "yet")
                messages.error(request, _("One or more shipments for {order} "
                                          "haven't been yet created").format(
                    order=shipping_service.order))

            orders.append(shipping_service.order)

        if files:
            log.debug("has files, making zip")
            tmp = tempfile.mkstemp(suffix=".zip")
            tf = zipfile.ZipFile(tmp[1], mode="w")
            for fileobj in files:
                filename = os.path.basename(fileobj.name)
                tf.write(fileobj.name, filename)
            tf.close()

            response = HttpResponse(File(file(tmp[1])), mimetype="application/zip")
            response['Content-disposition'] = ('attachment; '
                                               'filename='
                                               '"labels_for_orders_{}.zip"').format(
                "-".join(str(o.id) for o in orders))
        else:
            log.debug("no files..")
            response = HttpResponseRedirect(request.path_info)
        return response