예제 #1
0
class RcvCsvRowSchema(marshmallow.Schema):

    EXPECTED_INPUT_FIELDS = tuple(_RCV_CSV_EXPECTED_FIELD_NAMES) + (_CSV_ROW_DICT_EXTRA_FIELDS_KEY, )  # type: ignore  # noqa: E501
    FIELD_FECHA_RECEPCION_DATETIME_TZ = tz_utils.TZ_CL_SANTIAGO

    class Meta:
        strict = True

    emisor_rut = mm_fields.RutField(
        required=True,
        load_from='RUT Proveedor',
    )
    tipo_dte = marshmallow.fields.Integer(
        required=True,
        load_from='Tipo Doc',
    )
    folio = marshmallow.fields.Integer(
        required=True,
        load_from='Folio',
    )
    fecha_emision_date = mm_utils.CustomMarshmallowDateField(
        format='%d/%m/%Y',  # e.g. '22/10/2018'
        required=True,
        load_from='Fecha Docto',
    )
    fecha_recepcion_datetime = marshmallow.fields.DateTime(
        format='%d/%m/%Y %H:%M:%S',  # e.g. '23/10/2018 01:54:13'
        required=True,
        load_from='Fecha Recepcion',
    )
    # note: this field value is set using data passed in the schema context.
    receptor_rut = mm_fields.RutField(
        required=True,
    )
    monto_total = marshmallow.fields.Integer(
        required=True,
        load_from='Monto Total',
    )

    @marshmallow.pre_load
    def preprocess(self, in_data: dict) -> dict:
        # note: required fields checks are run later on automatically thus we may not assume that
        #   values of required fields (`required=True`) exist.

        # Set field value only if it was not in the input data.
        in_data.setdefault('receptor_rut', self.context['receptor_rut'])

        return in_data

    @marshmallow.post_load
    def postprocess(self, data: dict) -> dict:
        # >>> data['fecha_recepcion_datetime'].isoformat()
        # '2018-10-23T01:54:13'
        data['fecha_recepcion_datetime'] = tz_utils.convert_naive_dt_to_tz_aware(
            dt=data['fecha_recepcion_datetime'], tz=self.FIELD_FECHA_RECEPCION_DATETIME_TZ)
        # >>> data['fecha_recepcion_datetime'].isoformat()
        # '2018-10-23T01:54:13-03:00'
        # >>> data['fecha_recepcion_datetime'].astimezone(pytz.UTC).isoformat()
        # '2018-10-23T04:54:13+00:00'

        # note: to express this value in another timezone (but the value does not change), do
        #   `datetime_obj.astimezone(pytz.timezone('some timezone'))`

        return data

    @marshmallow.validates_schema(pass_original=True)
    def validate_schema(self, data: dict, original_data: dict) -> None:
        # Fail validation if there was an unexpected input field.
        unexpected_input_fields = (
            set(original_data)
            - set(self.fields)
            - set(self.EXPECTED_INPUT_FIELDS)
        )
        if unexpected_input_fields:
            raise marshmallow.ValidationError(
                'Unexpected input field', field_names=list(unexpected_input_fields))

    # @marshmallow.validates('field_x')
    # def validate_field_x(self, value):
    #     pass

    ###########################################################################
    # non-marshmallow-related methods
    ###########################################################################

    def deserialize_csv_row(self, row: OrderedDict) -> dict:
        try:
            result = self.load(row)  # type: marshmallow.UnmarshalResult
        except marshmallow.ValidationError as exc:
            exc_msg = "Validation errors during deserialization."
            validation_error_msgs = dict(exc.normalized_messages())
            raise ValueError(exc_msg, validation_error_msgs) from exc

        result_data = result.data  # type: dict
        result_errors = result.errors  # type: dict
        if result_errors:
            raise Exception("Deserialization errors: %s", result_errors)
        return result_data
예제 #2
0
class RcvCompraPendienteCsvRowSchema(_RcvCsvRowSchemaBase):

    FIELD_FECHA_RECEPCION_DT_TZ = SII_OFFICIAL_TZ
    FIELD_FECHA_ACUSE_DT_TZ = SII_OFFICIAL_TZ

    class Meta:
        strict = True

    ###########################################################################
    # basic fields
    ###########################################################################

    emisor_rut = mm_fields.RutField(
        required=True,
        load_from='RUT Proveedor',
    )
    tipo_docto = mm_fields.RcvTipoDoctoField(
        required=True,
        load_from='Tipo Doc',
    )
    folio = marshmallow.fields.Integer(
        required=True,
        load_from='Folio',
    )
    fecha_emision_date = mm_utils.CustomMarshmallowDateField(
        format='%d/%m/%Y',  # e.g. '22/10/2018'
        required=True,
        load_from='Fecha Docto',
    )
    monto_total = marshmallow.fields.Integer(
        required=True,
        load_from='Monto Total',
    )
    emisor_razon_social = marshmallow.fields.String(
        required=True,
        load_from='Razon Social',
    )

    ###########################################################################
    # fields whose value is set using data passed in the schema context
    ###########################################################################

    receptor_rut = mm_fields.RutField(
        required=True,
    )
    receptor_razon_social = marshmallow.fields.String(
        required=True,
    )

    ###########################################################################
    # extra fields: not included in the returned struct
    ###########################################################################

    fecha_recepcion_dt = marshmallow.fields.DateTime(
        format='%d/%m/%Y %H:%M:%S',  # e.g. '23/10/2018 01:54:13'
        required=True,
        load_from='Fecha Recepcion',
    )

    @marshmallow.pre_load
    def preprocess(self, in_data: dict) -> dict:
        # note: required fields checks are run later on automatically thus we may not assume that
        #   values of required fields (`required=True`) exist.

        # Set field value only if it was not in the input data.
        in_data.setdefault('receptor_rut', self.context['receptor_rut'])
        in_data.setdefault('receptor_razon_social', self.context['receptor_razon_social'])

        # Fix missing/default values.
        if 'Fecha Acuse' in in_data:
            if in_data['Fecha Acuse'] == '':
                in_data['Fecha Acuse'] = None

        return in_data

    @marshmallow.post_load
    def postprocess(self, data: dict) -> dict:
        # >>> data['fecha_recepcion_dt'].isoformat()
        # '2018-10-23T01:54:13'
        data['fecha_recepcion_dt'] = tz_utils.convert_naive_dt_to_tz_aware(
            dt=data['fecha_recepcion_dt'], tz=self.FIELD_FECHA_RECEPCION_DT_TZ)
        # >>> data['fecha_recepcion_dt'].isoformat()
        # '2018-10-23T01:54:13-03:00'
        # >>> data['fecha_recepcion_dt'].astimezone(pytz.UTC).isoformat()
        # '2018-10-23T04:54:13+00:00'

        # note: to express this value in another timezone (but the value does not change), do
        #   `dt_obj.astimezone(pytz.timezone('some timezone'))`

        return data

    def to_detalle_entry(self, data: dict) -> RcPendienteDetalleEntry:
        try:
            emisor_rut: Rut = data['emisor_rut']  # type: ignore
            tipo_docto = data['tipo_docto']  # type: ignore
            folio: int = data['folio']  # type: ignore
            fecha_emision_date: date = data['fecha_emision_date']  # type: ignore
            receptor_rut: Rut = data['receptor_rut']  # type: ignore
            monto_total: int = data['monto_total']  # type: ignore
            emisor_razon_social: str = data['emisor_razon_social']  # type: ignore
            receptor_razon_social: str = data['receptor_razon_social']  # type: ignore
            fecha_recepcion_dt: datetime = data['fecha_recepcion_dt']  # type: ignore
        except KeyError as exc:
            raise ValueError("Programming error: a referenced field is missing.") from exc

        try:
            detalle_entry = RcPendienteDetalleEntry(
                emisor_rut=emisor_rut,
                tipo_docto=tipo_docto,
                folio=folio,
                fecha_emision_date=fecha_emision_date,
                receptor_rut=receptor_rut,
                monto_total=monto_total,
                emisor_razon_social=emisor_razon_social,
                receptor_razon_social=receptor_razon_social,
                fecha_recepcion_dt=fecha_recepcion_dt,
            )
        except (TypeError, ValueError):
            raise

        return detalle_entry