diff --git a/correios/client.py b/correios/client.py index e78c9ea..ba67cd1 100644 --- a/correios/client.py +++ b/correios/client.py @@ -15,16 +15,21 @@ import os from datetime import datetime -from typing import Union, Sequence, List, Dict +from decimal import Decimal +from typing import Union, Sequence, List, Dict, Optional from correios import xml_utils, DATADIR from correios.exceptions import PostingListSerializerError, TrackingCodesLimitExceededError +from correios.models.data import EXTRA_SERVICE_MP, EXTRA_SERVICE_AR +from correios.utils import to_decimal, to_integer from .models.address import ZipAddress, ZipCode from .models.posting import (NotFoundTrackingEvent, TrackingCode, PostingList, ShippingLabel, - TrackingEvent, EventStatus) -from .models.user import User, FederalTaxNumber, StateTaxNumber, Contract, PostingCard, Service + TrackingEvent, EventStatus, Package, Freight, FreightError) +from .models.user import User, FederalTaxNumber, StateTaxNumber, Contract, PostingCard, Service, ExtraService from .soap import SoapClient +KG = 1000 # g + class ModelBuilder: def build_service(self, service_data): @@ -149,6 +154,38 @@ def load_tracking_events(self, tracking_codes: Dict[str, TrackingCode], response return result + def build_freights_list(self, response): + result = [] + for service_data in response.Servicos.cServico: + service = Service.get(service_data.Codigo) + error_code = to_integer(service_data.Erro) + if error_code: + freight = FreightError( + service=service, + error_code=error_code, + error_message=service_data.MsgErro, + ) + else: + delivery_time = int(service_data.PrazoEntrega) + value = to_decimal(service_data.ValorSemAdicionais) + declared_value = to_decimal(service_data.ValorValorDeclarado) + ar_value = to_decimal(service_data.ValorAvisoRecebimento) + mp_value = to_decimal(service_data.ValorMaoPropria) + saturday = service_data.EntregaSabado or "" + home = service_data.EntregaDomiciliar or "" + freight = Freight( + service=service, + delivery_time=delivery_time, + value=value, + declared_value=declared_value, + ar_value=ar_value, + mp_value=mp_value, + saturday=saturday.upper() == "S", + home=home.upper() == "S", + ) + result.append(freight) + return result + class PostingListSerializer: def _get_posting_list_element(self, posting_list): @@ -278,6 +315,7 @@ class Correios: 'test': ("https://apphom.correios.com.br/SigepMasterJPA/AtendeClienteService/AtendeCliente?wsdl", False), } websro_url = "https://webservice.correios.com.br/service/rastro/Rastro.wsdl" + freight_url = "http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx?WSDL" def __init__(self, username, password, timeout=8, environment="production"): self.username = username @@ -294,6 +332,9 @@ def __init__(self, username, password, timeout=8, environment="production"): self.websro_client = SoapClient(self.websro_url, timeout=self.timeout) self.websro = self.websro_client.service + self.freight_client = SoapClient(self.freight_url, timeout=self.timeout) + self.freight = self.freight_client.service + self.model_builder = ModelBuilder() def _auth_call(self, method_name, *args, **kwargs): @@ -380,3 +421,39 @@ def get_tracking_code_events(self, tracking_list): response = self.websro.buscaEventosLista(self.username, self.password, "L", "T", "101", tuple(tracking_codes.keys())) return self.model_builder.load_tracking_events(tracking_codes, response) + + def calculate_freights(self, + posting_card: PostingCard, + services: List[Union[Service, int]], + from_zip: Union[ZipCode, int, str], to_zip: Union[ZipCode, int, str], + package: Package, + value: Union[Decimal, float] = 0.00, + extra_services: Optional[Sequence[Union[ExtraService, int]]] = None): + + administrative_code = posting_card.administrative_code + services = [Service.get(s) for s in services] + from_zip = ZipCode.create(from_zip) + to_zip = ZipCode.create(to_zip) + + if extra_services is None: + extra_services = [] + else: + extra_services = [ExtraService.get(es) for es in extra_services] + + response = self.freight.CalcPrecoPrazo( + administrative_code, + self.password, + ",".join(str(s) for s in services), + str(from_zip), + str(to_zip), + package.weight / KG, + package.package_type, + package.length, + package.height, + package.width, + package.diameter, + "S" if EXTRA_SERVICE_MP in extra_services else "N", + value, + "S" if EXTRA_SERVICE_AR in extra_services else "N", + ) + return self.model_builder.build_freights_list(response) diff --git a/correios/models/posting.py b/correios/models/posting.py index f345c29..f8aa20e 100644 --- a/correios/models/posting.py +++ b/correios/models/posting.py @@ -15,14 +15,15 @@ import os import math -from datetime import datetime +from datetime import datetime, timedelta from decimal import Decimal -from typing import Optional, Sequence, Tuple, Union, List, Dict # noqa: F401 +from typing import Optional, Tuple, Union, List, Dict # noqa: F401 from PIL import Image from correios import DATADIR from correios import exceptions +from correios.utils import to_decimal from .address import Address, ZipCode from .data import SERVICE_PAC, TRACKING_EVENT_TYPES, TRACKING_STATUS from .user import Contract # noqa: F401 @@ -255,6 +256,12 @@ class Package: TYPE_BOX = 2 # type: int TYPE_CYLINDER = 3 # type: int + freight_package_types = { + TYPE_BOX: 1, + TYPE_CYLINDER: 2, + TYPE_ENVELOPE: 3, + } # type: Dict[int, int] + def __init__(self, package_type: int = TYPE_BOX, width: Union[float, int] = 0, # cm @@ -336,6 +343,19 @@ def volumetric_weight(self) -> int: def posting_weight(self) -> int: return Package.calculate_posting_weight(self.weight, self.volumetric_weight) + @property + def freight_package_type(self) -> int: + """ + SIGEP API and Freight API different codes to identify package types: + + SIGEP | Freight | Type + ------+---------+---------- + 1 | 3 | Envelope + 2 | 1 | Box + 3 | 2 | Cylinder + """ + return self.freight_package_types[self.package_type] + @classmethod def calculate_volumetric_weight(cls, width, height, length) -> int: return int(math.ceil((width * height * length) / IATA_COEFICIENT)) @@ -355,7 +375,7 @@ def calculate_insurance(cls, per_unit_value = Decimal(per_unit_value) if Service.get(service) == Service.get(SERVICE_PAC) and per_unit_value > INSURANCE_VALUE_THRESHOLD: value = (per_unit_value - INSURANCE_VALUE_THRESHOLD) * INSURANCE_PERCENTUAL_COST - return Decimal(value * quantity).quantize(Decimal('0.00')) + return to_decimal(value * quantity) @classmethod def validate(cls, @@ -457,7 +477,7 @@ def __init__(self, service: Union[Service, int], tracking_code: Union[TrackingCode, str], package: Package, - extra_services: Optional[Sequence[Union[ExtraService, int]]] = None, + extra_services: Optional[List[Union[ExtraService, int]]] = None, logo: Optional[Union[str, Image.Image]] = None, order: Optional[str] = "", invoice_number: Optional[str] = "", @@ -506,7 +526,7 @@ def __init__(self, def __repr__(self): return "".format(str(self.tracking_code)) - def add_extra_services(self, extra_services: Sequence[Union["ExtraService", int]]): + def add_extra_services(self, extra_services: List[Union["ExtraService", int]]): for extra_service in extra_services: self.add_extra_service(extra_service) @@ -639,3 +659,56 @@ def close_with_id(self, number: int): @property def closed(self): return self.number is not None + + +class Freight: + def __init__(self, + service: Union[Service, int], + delivery_time: Union[int, timedelta], + value: Union[Decimal, float, int, str], + declared_value: Union[Decimal, float, int, str] = 0.00, + mp_value: Union[Decimal, float, int, str] = 0.00, + ar_value: Union[Decimal, float, int, str] = 0.00, + saturday: bool = False, + home: bool = False) -> None: + + self.service = Service.get(service) + + if not isinstance(delivery_time, timedelta): + delivery_time = timedelta(days=delivery_time) + self.delivery_time = delivery_time + + if not isinstance(value, Decimal): + value = to_decimal(value) + self.value = value + + if not isinstance(declared_value, Decimal): + declared_value = to_decimal(declared_value) + self.declared_value = declared_value + + if not isinstance(mp_value, Decimal): + mp_value = to_decimal(mp_value) + self.mp_value = mp_value + + if not isinstance(ar_value, Decimal): + ar_value = to_decimal(ar_value) + self.ar_value = ar_value + + self.saturday = saturday + self.home = home + self.error_code = 0 + self.error_message = "" + + @property + def total(self): + return self.value + self.declared_value + self.ar_value + self.mp_value + + +class FreightError(Freight): + def __init__(self, + service: Union[Service, int], + error_code: Union[str, int], + error_message: str) -> None: + super().__init__(service=service, delivery_time=0, value=Decimal("0.00")) + self.error_code = int(error_code) + self.error_message = error_message diff --git a/correios/models/user.py b/correios/models/user.py index 2ce6962..8c30ccd 100644 --- a/correios/models/user.py +++ b/correios/models/user.py @@ -13,37 +13,23 @@ # limitations under the License. -import os -from datetime import datetime +from datetime import datetime # noqa: F401 from decimal import Decimal -from typing import Union, Optional, Sequence +from typing import Union, Optional, Sequence, List # noqa: F401 +import os from PIL import Image from correios import DATADIR from correios.exceptions import (InvalidFederalTaxNumberError, InvalidExtraServiceError, InvalidRegionalDirectionError, InvalidUserContractError, MaximumDeclaredValueError, MinimumDeclaredValueError) +from correios.utils import to_integer, to_datetime from .data import EXTRA_SERVICES, REGIONAL_DIRECTIONS, SERVICES, EXTRA_SERVICE_VD EXTRA_SERVICE_CODE_SIZE = 2 -def to_integer(number: Union[int, str]) -> int: - try: - return int(number.strip()) # type: ignore - except AttributeError: - return int(number) - - -def to_datetime(date: Union[datetime, str], fmt="%Y-%m-%d %H:%M:%S%z") -> datetime: - if isinstance(date, str): - last_colon_pos = date.rindex(":") - date = date[:last_colon_pos] + date[last_colon_pos + 1:] - return datetime.strptime(date, fmt) - return date - - def _to_federal_tax_number(federal_tax_number) -> "FederalTaxNumber": if isinstance(federal_tax_number, FederalTaxNumber): return federal_tax_number @@ -155,8 +141,9 @@ def __init__(self, self.max_declared_value = max_declared_value if default_extra_services is None: - default_extra_services = [] - self.default_extra_services = [ExtraService.get(es) for es in default_extra_services] + self.default_extra_services = [] # type: List + else: + self.default_extra_services = [ExtraService.get(es) for es in default_extra_services] def __str__(self): return str(self.code) @@ -165,6 +152,7 @@ def __repr__(self): return "".format(self.code, self.display_name) def __eq__(self, other): + other = Service.get(other) return (self.id, self.code) == (other.id, other.code) def validate_declared_value(self, value: Union[Decimal, float]) -> bool: @@ -215,10 +203,12 @@ def __repr__(self): return "".format(self.number, self.code) def __eq__(self, other): + if isinstance(other, int): + return self.number == other return self.number == other.number def is_declared_value(self): - return self.number == EXTRA_SERVICE_VD + return self == EXTRA_SERVICE_VD @classmethod def get(cls, number: Union['ExtraService', int]) -> 'ExtraService': diff --git a/correios/soap.py b/correios/soap.py index ed035f3..09bec74 100644 --- a/correios/soap.py +++ b/correios/soap.py @@ -65,5 +65,5 @@ def send(self, request): class SoapClient(Client): def __init__(self, url, cert=None, verify=True, timeout=8, *args, **kwargs): transport = RequestsTransport(cert=cert, verify=verify, timeout=timeout) - headers = {"Content-Type": "text/xml;charset=UTF-8", "SOAPAction": ""} + headers = {"Content-Type": "text/xml;charset=UTF-8"} super().__init__(url, transport=transport, headers=headers, **kwargs) diff --git a/correios/utils.py b/correios/utils.py index 4b50e47..9fbcedf 100644 --- a/correios/utils.py +++ b/correios/utils.py @@ -1,5 +1,9 @@ +from datetime import datetime +from decimal import Decimal from itertools import chain -from typing import Container, Iterable, Sized +from typing import Container, Iterable, Sized, Union + +import re def capitalize_phrase(phrase: str) -> str: @@ -51,3 +55,29 @@ def __contains__(self, elem): def __len__(self): return sum(len(r) for r in self.ranges) + + +def to_integer(number: Union[int, str]) -> int: + return int(str(number).strip()) + + +def to_datetime(date: Union[datetime, str], fmt="%Y-%m-%d %H:%M:%S%z") -> datetime: + if isinstance(date, str): + last_colon_pos = date.rindex(":") + date = date[:last_colon_pos] + date[last_colon_pos + 1:] + return datetime.strptime(date, fmt) + return date + + +def to_decimal(value: Union[Decimal, str, float], precision=2): + if not isinstance(value, Decimal): + value = rreplace(str(value), ",", ".", 1) + if "." in value: + real, imag = value.rsplit(".", 1) + else: + real, imag = value, "0" + real = re.sub("[,._]", "", real) + value = Decimal("{}.{}".format(real, imag)) + + quantize = Decimal("0." + "0" * precision) + return value.quantize(quantize) diff --git a/documentation/Manual-de-Implementacao-do-Calculo-Remoto-de-Precos-e-Prazos.pdf b/documentation/Manual-de-Implementacao-do-Calculo-Remoto-de-Precos-e-Prazos.pdf new file mode 100644 index 0000000..6ba2f93 Binary files /dev/null and b/documentation/Manual-de-Implementacao-do-Calculo-Remoto-de-Precos-e-Prazos.pdf differ diff --git a/tests/conftest.py b/tests/conftest.py index 917b890..5af1b5a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -160,7 +160,7 @@ class Meta: package_type = Package.TYPE_BOX width = LazyFunction(lambda: random.randint(11, 30)) height = LazyFunction(lambda: random.randint(2, 30)) - length = LazyFunction(lambda: random.randint(16, 30)) + length = LazyFunction(lambda: random.randint(18, 30)) weight = LazyFunction(lambda: random.randint(1, 100) * 100) service = LazyFunction(lambda: random.choice(_services)) sequence = Sequence(lambda n: (n, n + 1)) diff --git a/tests/fixtures/cassettes/test_basic_client.yaml b/tests/fixtures/cassettes/test_basic_client.yaml index 35c506b..3f76629 100644 --- a/tests/fixtures/cassettes/test_basic_client.yaml +++ b/tests/fixtures/cassettes/test_basic_client.yaml @@ -5,136 +5,134 @@ interactions: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] - User-Agent: [python-requests/2.10.0] + User-Agent: [python-requests/2.13.0] method: GET uri: https://apphom.correios.com.br/SigepMasterJPA/AtendeClienteService/AtendeCliente?wsdl response: body: string: !!binary | - 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 - PUckbwEA + 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 + ZWHydfj4S/63ix+8wKOYxB//DUzY59RDZwEA headers: Connection: [Keep-Alive] Content-Encoding: [gzip] - Content-Length: ['6675'] + Content-Length: ['6582'] Content-Type: [text/xml;charset=UTF-8] - Date: ['Mon, 25 Jul 2016 05:12:25 GMT'] + Date: ['Wed, 19 Apr 2017 17:02:57 GMT'] Keep-Alive: ['timeout=5, max=100'] Vary: ['Accept-Encoding,User-Agent'] status: {code: 200, message: OK} @@ -144,7 +142,7 @@ interactions: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] - User-Agent: [python-requests/2.10.0] + User-Agent: [python-requests/2.13.0] method: GET uri: https://webservice.correios.com.br/service/rastro/Rastro.wsdl response: @@ -157,44 +155,22 @@ interactions: http://schemas.xmlsoap.org/wsdl/soap/\">\n \n \n \ \ \n \n \n\ - \ \n \n \n \n \n \n \n\ - \ \n \n \n \n \n\ - \ \n \n \n \n\ - \ \n \n \n \n \n \n \ - \ \n \n \n \n \n \ - \ \n \n \n \n \n\ - \ \n \n \n \n \n \n \ - \ \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\ + \ \n \n \ + \ \n \ + \ \n \n \n \n \n \n \n \n \n \n \n\ - \ \n \ - \ \n \n\ - \ \n \n \n \n \n \n \n \n \n\ - \ \n \ - \ \n \n\ - \ \n \n \n \n \n \n \n \n \n \n \n \n \ @@ -203,30 +179,20 @@ interactions: \ \n \n \n \n\ \ \n \n \n \ - \ \n \n \n \ - \ \n \n \n \n \n \n \n \n \n \n \n\ - \ \n \n \n \ - \ \n \n \n \ - \ \n \n \n \n \ - \ \n \n \n \ - \ \n \n \n \n\ - \ \n \ - \ \n \n \n \n \n \ + \ \n \n \n\n"} headers: Cache-Control: ['no-cache="set-cookie, set-cookie2"'] Connection: [Keep-Alive] Content-Language: [pt-BR] Content-Type: [text/xml] - Date: ['Mon, 25 Jul 2016 05:12:25 GMT'] + Date: ['Wed, 19 Apr 2017 17:07:56 GMT'] Expires: ['Thu, 01 Dec 1994 16:00:00 GMT'] - Keep-Alive: ['timeout=10, max=82'] - Set-Cookie: [_op_aixPageId=a2_58acace0-287e-46b9-adb9-ee8875a15b51; Path=/] - X-OPNET-Transaction-Trace: [a2_58acace0-287e-46b9-adb9-ee8875a15b51] + Keep-Alive: ['timeout=2, max=99851'] + Set-Cookie: [_op_aixPageId=a2_4cda5bfa-cf14-4c67-8e42-5ae4bbf42acb; Path=/] + X-OPNET-Transaction-Trace: [a2_4cda5bfa-cf14-4c67-8e42-5ae4bbf42acb] X-Powered-By: [Servlet/3.0] status: {code: 200, message: OK} - request: @@ -235,39 +201,33 @@ interactions: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] - Cookie: [_op_aixPageId=a2_58acace0-287e-46b9-adb9-ee8875a15b51] - User-Agent: [python-requests/2.10.0] + Cookie: [_op_aixPageId=a2_4cda5bfa-cf14-4c67-8e42-5ae4bbf42acb] + User-Agent: [python-requests/2.13.0] method: GET uri: https://webservice.correios.com.br/service/rastro/Rastro_schema1.xsd response: body: {string: "\n\n \n\ - \n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n \n \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n\n \n\ + \n \n\n \n\n \n\ + \n \n\ + \n \n\n \n\n \n\n \n \n \n \n \n \n \n \ + \ \n \n \n\n \n \n \n \n\ @@ -336,67 +296,693 @@ interactions: \ \n \n\n \n \n \n \n \n\ - \n \n \n \ - \ \n\ - \ \n \n\n \n \n \n \n \n\ - \n \n \n \ - \ \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n \n \n \n \n \n\n \n \n \n \n \ - \ \n \n \n \n \n \ - \ \n\n \n \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n\ - \ \n \n\n \n \n \n \n \n\ - \n \n \n \n \ - \ \n \n\n \n \n \n \n\ - \ \n\n \n \ - \ \n \n \n \n \n \n \n\ - \ \n \n \n"} + "} headers: Cache-Control: ['no-cache="set-cookie, set-cookie2"'] Connection: [Keep-Alive] Content-Language: [pt-BR] Content-Type: [text/xml] - Date: ['Mon, 25 Jul 2016 05:12:26 GMT'] + Date: ['Wed, 19 Apr 2017 17:07:56 GMT'] Expires: ['Thu, 01 Dec 1994 16:00:00 GMT'] - Keep-Alive: ['timeout=10, max=93'] - Set-Cookie: [_op_aixPageId=a2_1dfbb45d-33ce-412b-ab99-8e8bb7e87010; Path=/] - X-OPNET-Transaction-Trace: [a2_1dfbb45d-33ce-412b-ab99-8e8bb7e87010] + Keep-Alive: ['timeout=2, max=99980'] + Set-Cookie: [_op_aixPageId=a2_c703d1ef-d92e-4b04-9e35-8004c730faae; Path=/] + X-OPNET-Transaction-Trace: [a2_c703d1ef-d92e-4b04-9e35-8004c730faae] X-Powered-By: [Servlet/3.0] status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx?WSDL + response: + body: {string: "\r\n\r\n \r\n \ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n\ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n\ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\ + \n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \ + \ \r\n \r\n \ + \ \r\ + \n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n\ + \ \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n\ + \ \r\n \ + \ \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \ + \ \r\n \r\n \r\ + \n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \ + \ \r\n \r\n \ + \ \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \ + \ \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \ + \ \r\n \r\n \ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n\ + \ \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n\ + \ \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \ + \ \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n\ + \ \r\n \r\n \r\ + \n \r\n \r\n\ + \ \r\n \r\ + \n \r\n\ + \ \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n \r\n \r\ + \n \r\n \r\n \r\n \r\n"} + headers: + Cache-Control: ['private, max-age=0'] + Content-Length: ['47223'] + Content-Type: [text/xml; charset=utf-8] + Date: ['Wed, 19 Apr 2017 17:08:24 GMT'] + Server: [Microsoft-IIS/7.5] + X-AspNet-Version: [4.0.30319] + X-Powered-By: [ASP.NET] + status: {code: 200, message: OK} version: 1 diff --git a/tests/fixtures/cassettes/test_calculate_freight_with_error.yaml b/tests/fixtures/cassettes/test_calculate_freight_with_error.yaml new file mode 100644 index 0000000..3ca7c27 --- /dev/null +++ b/tests/fixtures/cassettes/test_calculate_freight_with_error.yaml @@ -0,0 +1,33 @@ +interactions: +- request: + body: 08082650n5f9t840096999990009999999980.0230282016N0.0N + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['909'] + Content-Type: [text/xml;charset=UTF-8] + SOAPAction: + - !!binary | + Imh0dHA6Ly90ZW1wdXJpLm9yZy9DYWxjUHJlY29QcmF6byI= + User-Agent: [python-requests/2.13.0] + method: POST + uri: http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx + response: + body: {string: '400960,0000,000,000,00-4Peso excedido.0,00'} + headers: + Cache-Control: ['private, max-age=0'] + Content-Length: ['759'] + Content-Type: [text/xml; charset=utf-8] + Date: ['Tue, 25 Apr 2017 18:02:42 GMT'] + Server: [Microsoft-IIS/7.5] + X-AspNet-Version: [4.0.30319] + X-Powered-By: [ASP.NET] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/fixtures/cassettes/test_calculate_freights.yaml b/tests/fixtures/cassettes/test_calculate_freights.yaml new file mode 100644 index 0000000..550f215 --- /dev/null +++ b/tests/fixtures/cassettes/test_calculate_freights.yaml @@ -0,0 +1,33 @@ +interactions: +- request: + body: 08082650n5f9t840096,4106807192100800300010.0224282616N0.0N + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['914'] + Content-Type: [text/xml;charset=UTF-8] + SOAPAction: + - !!binary | + Imh0dHA6Ly90ZW1wdXJpLm9yZy9DYWxjUHJlY29QcmF6byI= + User-Agent: [python-requests/2.13.0] + method: POST + uri: http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx + response: + body: {string: '4009623,7510,000,000,00SS023,754106814,1060,000,000,00SN014,10'} + headers: + Cache-Control: ['private, max-age=0'] + Content-Length: ['1155'] + Content-Type: [text/xml; charset=utf-8] + Date: ['Tue, 25 Apr 2017 16:47:47 GMT'] + Server: [Microsoft-IIS/7.5] + X-AspNet-Version: [4.0.30319] + X-Powered-By: [ASP.NET] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/fixtures/cassettes/test_calculate_freights_with_extra_services.yaml b/tests/fixtures/cassettes/test_calculate_freights_with_extra_services.yaml new file mode 100644 index 0000000..5652256 --- /dev/null +++ b/tests/fixtures/cassettes/test_calculate_freights_with_extra_services.yaml @@ -0,0 +1,32 @@ +interactions: +- request: + body: 08082650n5f9t84009607192100800300010.0218231916S9000.00S + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['912'] + Content-Type: [text/xml;charset=UTF-8] + SOAPAction: + - !!binary | + Imh0dHA6Ly90ZW1wdXJpLm9yZy9DYWxjUHJlY29QcmF6byI= + User-Agent: [python-requests/2.13.0] + method: POST + uri: http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx + response: + body: {string: '4009696,0315,504,3062,48SS023,75'} + headers: + Cache-Control: ['private, max-age=0'] + Content-Length: ['773'] + Content-Type: [text/xml; charset=utf-8] + Date: ['Tue, 25 Apr 2017 19:17:27 GMT'] + Server: [Microsoft-IIS/7.5] + X-AspNet-Version: [4.0.30319] + X-Powered-By: [ASP.NET] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/fixtures/cassettes/test_get_tracking_code_events_withou_city_field.yaml b/tests/fixtures/cassettes/test_get_tracking_code_events_without_city_field.yaml similarity index 100% rename from tests/fixtures/cassettes/test_get_tracking_code_events_withou_city_field.yaml rename to tests/fixtures/cassettes/test_get_tracking_code_events_without_city_field.yaml diff --git a/tests/test_client.py b/tests/test_client.py index a4cdb72..14cc03a 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -13,13 +13,16 @@ # limitations under the License. +from decimal import Decimal + import pytest from correios.exceptions import PostingListSerializerError, TrackingCodesLimitExceededError from correios.models.address import ZipCode -from correios.models.data import SERVICE_SEDEX10, SERVICE_SEDEX, EXTRA_SERVICE_VD +from correios.models.data import (SERVICE_SEDEX10, SERVICE_SEDEX, EXTRA_SERVICE_VD, SERVICE_PAC, EXTRA_SERVICE_AR, + EXTRA_SERVICE_MP) from correios.models.posting import (NotFoundTrackingEvent, PostingList, ShippingLabel, - TrackingCode) + TrackingCode, Package) from correios.models.user import PostingCard, Service, ExtraService from .vcr import vcr @@ -133,7 +136,7 @@ def test_get_tracking_code_events(client): @pytest.mark.skipif(not correios, reason="API Client support disabled") @vcr.use_cassette -def test_get_tracking_code_events_withou_city_field(client): +def test_get_tracking_code_events_without_city_field(client): result = client.get_tracking_code_events("PJ651329640BR") assert isinstance(result[0], TrackingCode) @@ -242,3 +245,61 @@ def test_limit_size_city_name(posting_list, shipping_label): assert b"" in xml assert b"" in xml + + +@pytest.mark.skipif(not correios, reason="API Client support disabled") +@vcr.use_cassette +def test_calculate_freights(client, posting_card, package): + freights = client.calculate_freights(posting_card, [SERVICE_SEDEX, SERVICE_PAC], "07192100", "80030001", package) + assert len(freights) == 2 + + freight = freights[0] + assert freight.error_code == 0 + assert freight.error_message == "" + assert freight.service == SERVICE_SEDEX + assert freight.delivery_time.days == 1 + assert freight.total == Decimal("23.75") + assert freight.saturday is True + assert freight.home is True + + freight = freights[1] + assert freight.error_code == 0 + assert freight.error_message == "" + assert freight.service == SERVICE_PAC + assert freight.delivery_time.days == 6 + assert freight.total == Decimal("14.10") + assert freight.saturday is False + assert freight.home is True + + +@pytest.mark.skipif(not correios, reason="API Client support disabled") +@vcr.use_cassette +def test_calculate_freights_with_extra_services(client, posting_card, package): + freights = client.calculate_freights( + posting_card=posting_card, + services=[SERVICE_SEDEX], + from_zip="07192100", + to_zip="80030001", + package=package, + value="9000.00", + extra_services=[EXTRA_SERVICE_AR, EXTRA_SERVICE_MP] + ) + assert len(freights) == 1 + + freight = freights[0] + assert freight.service == SERVICE_SEDEX + assert freight.total == Decimal("96.03") + assert freight.value == Decimal("23.75") + assert freight.declared_value == Decimal("62.48") + assert freight.mp_value == Decimal("5.50") + assert freight.ar_value == Decimal("4.30") + + +@pytest.mark.skipif(not correios, reason="API Client support disabled") +@vcr.use_cassette +def test_calculate_freight_with_error(client, posting_card, package: Package): + package.real_weight = 80000 # invalid weight (80kg) + freights = client.calculate_freights(posting_card, [SERVICE_SEDEX], "99999000", "99999999", package) + assert len(freights) == 1 + assert freights[0].error_code == -4 + assert freights[0].error_message == "Peso excedido." diff --git a/tests/test_posting_models.py b/tests/test_posting_models.py index 82fc692..c3b31ee 100644 --- a/tests/test_posting_models.py +++ b/tests/test_posting_models.py @@ -13,7 +13,7 @@ # limitations under the License. -from datetime import datetime +from datetime import datetime, timedelta from decimal import Decimal import os @@ -230,6 +230,16 @@ def test_package_basic(): service=SERVICE_PAC, # invalid tuple ) assert isinstance(package.service, Service) + assert package.package_type == posting.Package.TYPE_BOX + + +@pytest.mark.parametrize("package,freight_package_type", [ + (posting.Package(posting.Package.TYPE_ENVELOPE, 0, 0, 0, weight=1), 3), + (posting.Package(posting.Package.TYPE_BOX, 11, 10, 16, weight=1), 1), + (posting.Package(posting.Package.TYPE_CYLINDER, 0, 0, 14, 2, weight=1), 2), +]) +def test_freight_package_type(package, freight_package_type): + assert package.freight_package_type == freight_package_type def test_package_basic_envelop_dimensions_validation(): @@ -508,3 +518,18 @@ def test_basic_event_status(status_type, status_number): def test_invalid_event_status(event_type): with pytest.raises(exceptions.InvalidEventStatusError): posting.EventStatus(event_type, 1) + + +def test_basic_freight(): + freight = posting.Freight(SERVICE_SEDEX, timedelta(days=5), Decimal("10.00")) + assert freight.total == Decimal("10.00") + assert freight.delivery_time == timedelta(days=5) + assert freight.declared_value == Decimal("0.00") + assert freight.saturday is False + assert freight.home is False + + +def test_basic_freight_conversion(): + freight = posting.Freight(SERVICE_SEDEX, 5, 10.00) + assert freight.delivery_time == timedelta(days=5) + assert freight.total == Decimal("10.00") diff --git a/tests/test_user_models.py b/tests/test_user_models.py index 40ff8f6..ccf7408 100644 --- a/tests/test_user_models.py +++ b/tests/test_user_models.py @@ -257,6 +257,7 @@ def test_basic_extra_service(): assert extra_service.code == "AR" assert extra_service.name == "Aviso de Recebimento" assert repr(extra_service) == "" + assert extra_service == 1 def test_extra_service_sanitize_code(): diff --git a/tests/test_utils.py b/tests/test_utils.py index b04265a..0bf92cf 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,8 @@ +from decimal import Decimal + import pytest -from correios.utils import capitalize_phrase, RangeSet, rreplace +from correios.utils import capitalize_phrase, RangeSet, rreplace, to_decimal, to_integer phrase = 'FOo bAr BAZ qux' @@ -56,3 +58,41 @@ def test_rreplace(): assert rreplace(phrase, ' ', '-', 2) == 'foo bar-baz-qux' assert rreplace(phrase, ' ', '-', 3) == 'foo-bar-baz-qux' assert rreplace(phrase, ' ', '-') == 'foo-bar-baz-qux' + + +@pytest.mark.parametrize('s, d', ( + ("", Decimal("0.00")), + ("3", Decimal("3.00")), + ("3.57", Decimal("3.57")), + ("3.468", Decimal("3.47")), + ("3.4", Decimal("3.40")), + ("3,57", Decimal("3.57")), + ("3,468", Decimal("3.47")), + ("3,4", Decimal("3.40")), + ("1,357.93", Decimal("1357.93")), + ("1.357,93", Decimal("1357.93")), + ("1_357.93", Decimal("1357.93")), + ("1_357,93", Decimal("1357.93")), +)) +def test_to_decimal(s, d): + assert to_decimal(s) == d + + +@pytest.mark.parametrize('v, p, r', ( + ("3.4", 1, Decimal("3.4")), + ("3.4", 4, Decimal("3.4000")), + ("3.4", 0, Decimal("3")), + ("3.6", 0, Decimal("4")), + ("3.46876", 2, Decimal("3.47")), +)) +def test_to_decimal_precision(v, p, r): + assert to_decimal(v, p) == r + + +@pytest.mark.parametrize('v, r', ( + (3, 3), + ("3", 3), + (" \t3 \n", 3), +)) +def test_to_integer(v, r): + assert to_integer(v) == r diff --git a/tox.ini b/tox.ini index bbdc081..544d531 100644 --- a/tox.ini +++ b/tox.ini @@ -11,3 +11,4 @@ commands = [tox:travis] 3.5 = py35 +3.6 = py36