Add a charge to the account.

    :param account_id: The account on which to add the charge
    :param amount:  The amount of the charge
    :param reverses_id: Set this if this charge reverses another one
    :param product_code: A code identifying the type of product cnarged
    :param product_properties: A dict of hames and values.
    :return: The newly created charge.

def add_charge(account_id: str,
               amount: Money,
               reverses_id: Optional[str] = None,
               product_code: Optional[str] = None,
               product_properties: Optional[Dict[str, str]] = None) -> Charge:
    """
    Add a charge to the account.

    :param account_id: The account on which to add the charge
    :param amount:  The amount of the charge
    :param reverses_id: Set this if this charge reverses another one
    :param product_code: A code identifying the type of product cnarged
    :param product_properties: A dict of hames and values.
    :return: The newly created charge.
    """
    logger.info('adding-charge', account_id=account_id, amount=amount, product_code=product_code,
                product_properties=product_properties)

    with transaction.atomic():
        charge = Charge(account_id=account_id,
                        amount=amount)
        if reverses_id:
            charge.reverses_id = reverses_id
        if product_code:
            charge.product_code = product_code
        charge.full_clean(exclude=['id', 'account'])  # Exclude to avoid unnecessary db queries
        charge.save(force_insert=True)

        if product_properties:
            objs = [ProductProperty(charge=charge, name=k, value=v) for k, v in product_properties.items()]
            for o in objs:
                o.full_clean(exclude=['id', 'charge'])  # Exclude to avoid unnecessary db queries
            ProductProperty.objects.bulk_create(objs)

    return charge