Skip to content

Early-bird lifetime licence: $199 once, for the first 200 buyers only. See the offer

Receipt or invoice choice with ΑΦΜ and ΔΟΥ for Greek shops

Let WooCommerce customers choose Απόδειξη or Τιμολόγιο and, for invoices, enter a validated ΑΦΜ, ΔΟΥ, company name and business activity.

  • Type PHP
  • Runs Everywhere
PHP
// Receipt or invoice (Απόδειξη / Τιμολόγιο) with ΑΦΜ, ΔΟΥ, company name and activity.
// Block checkout, WooCommerce 9.9 or later.

// ΑΦΜ check-digit validation (shared with the "Validate a Greek VAT number" snippet).
if ( ! function_exists( 'snipfire_is_valid_afm' ) ) {
	function snipfire_is_valid_afm( $afm ) {
		$afm = preg_replace( '/[\s.\-]/', '', (string) $afm );
		$afm = preg_replace( '/^(EL|GR)/i', '', $afm );

		if ( ! preg_match( '/^[0-9]{9}$/', $afm ) || '000000000' === $afm ) {
			return false;
		}

		$sum = 0;
		for ( $i = 0; $i < 8; $i++ ) {
			$sum += (int) $afm[ $i ] * ( 2 ** ( 8 - $i ) );
		}

		return ( $sum % 11 ) % 10 === (int) $afm[8];
	}
}

add_action(
	'woocommerce_init',
	function () {
		if ( ! function_exists( 'woocommerce_register_additional_checkout_field' ) ) {
			return;
		}

		// Rule: the customer chose "Τιμολόγιο".
		$is_invoice = array(
			'type'       => 'object',
			'properties' => array(
				'checkout' => array(
					'type'       => 'object',
					'properties' => array(
						'additional_fields' => array(
							'type'       => 'object',
							'properties' => array(
								'snipfire/document-type' => array( 'const' => 'invoice' ),
							),
							'required'   => array( 'snipfire/document-type' ),
						),
					),
					'required'   => array( 'additional_fields' ),
				),
			),
		);

		// Rule: anything else (nothing chosen yet, or "Απόδειξη").
		$not_invoice = array(
			'type'       => 'object',
			'properties' => array(
				'checkout' => array(
					'properties' => array(
						'additional_fields' => array(
							'properties' => array(
								'snipfire/document-type' => array( 'not' => array( 'const' => 'invoice' ) ),
							),
						),
					),
				),
			),
		);

		woocommerce_register_additional_checkout_field(
			array(
				'id'       => 'snipfire/document-type',
				'label'    => 'Παραστατικό',
				'location' => 'order',
				'type'     => 'select',
				'required' => true,
				'options'  => array(
					array(
						'value' => 'receipt',
						'label' => 'Απόδειξη',
					),
					array(
						'value' => 'invoice',
						'label' => 'Τιμολόγιο',
					),
				),
			)
		);

		woocommerce_register_additional_checkout_field(
			array(
				'id'                => 'snipfire/vat-number',
				'label'             => 'ΑΦΜ',
				'location'          => 'order',
				'required'          => $is_invoice,
				'hidden'            => $not_invoice,
				'attributes'        => array(
					'autocomplete' => 'off',
					'maxLength'    => 11,
				),
				'sanitize_callback' => function ( $value ) {
					// Store 9 digits only: remove spaces, dots, dashes and an EL/GR prefix.
					$value = strtoupper( preg_replace( '/[\s.\-]/', '', sanitize_text_field( (string) $value ) ) );
					return preg_replace( '/^(EL|GR)/', '', $value );
				},
				'validate_callback' => function ( $value ) {
					if ( '' !== (string) $value && ! snipfire_is_valid_afm( $value ) ) {
						return new WP_Error( 'snipfire_invalid_afm', 'Ο ΑΦΜ δεν είναι έγκυρος. Ελέγξτε ότι έχει 9 ψηφία.' );
					}
				},
			)
		);

		woocommerce_register_additional_checkout_field(
			array(
				'id'       => 'snipfire/tax-office',
				'label'    => 'ΔΟΥ',
				'location' => 'order',
				'required' => $is_invoice,
				'hidden'   => $not_invoice,
			)
		);

		woocommerce_register_additional_checkout_field(
			array(
				'id'         => 'snipfire/company-name',
				'label'      => 'Επωνυμία εταιρείας',
				'location'   => 'order',
				'required'   => $is_invoice,
				'hidden'     => $not_invoice,
				'attributes' => array( 'autocomplete' => 'organization' ),
			)
		);

		woocommerce_register_additional_checkout_field(
			array(
				'id'       => 'snipfire/business-activity',
				'label'    => 'Δραστηριότητα',
				'location' => 'order',
				'required' => $is_invoice,
				'hidden'   => $not_invoice,
			)
		);
	}
);

// If "Απόδειξη" was chosen, don't keep invoice details typed before switching.
add_action(
	'woocommerce_store_api_checkout_update_order_from_request',
	function ( $order ) {
		$prefix = '_wc_other/'; // WooCommerce's meta key prefix for order fields.

		if ( 'invoice' === $order->get_meta( $prefix . 'snipfire/document-type' ) ) {
			return;
		}

		foreach ( array( 'vat-number', 'tax-office', 'company-name', 'business-activity' ) as $field ) {
			$order->delete_meta_data( $prefix . 'snipfire/' . $field );
		}
	}
);

What it does

Greek customers need to choose between a receipt (Απόδειξη) and an invoice (Τιμολόγιο). For an invoice you need the VAT number (ΑΦΜ), tax office (ΔΟΥ), company name and business activity.

This snippet adds a "Παραστατικό" choice to the block checkout. The four invoice fields appear, and become required, only when the customer picks "Τιμολόγιο". The ΑΦΜ is checked with the official check-digit rule and saved as 9 digits.

It uses WooCommerce's Additional Checkout Fields API, so the values are stored on the order the HPOS-safe way and show on the order screen, in order emails and on the thank-you page.

Good to know

  • Block checkout only, WooCommerce 9.9 or later. The classic (shortcode) checkout doesn't show these fields.
  • The fields appear in the "Additional order information" section of the checkout. Move that block in the Site Editor if you want them higher up.
  • Read a value in your own code with $order->get_meta( '_wc_other/snipfire/vat-number' ).

Move your snippets over this afternoon.

Importing changes nothing until you switch over, and your old shortcodes keep working.